1 INTRODUCTION

nhanes <- read.csv("https://jmusa3.github.io/jmusa/nhanes.csv")

#CREATE MISSING VALUES in some variables

# Set seed for reproducibility
set.seed(123)  

# Determine the number of missing values (15% of total rows)
num_missing <- round(0.14 * nrow(nhanes))

# Randomly select row indices to introduce missing values 
missing_indices <- sample(1:nrow(nhanes), num_missing, replace = FALSE)

# Set selected rows in body.weight to NA
nhanes$body.weight[missing_indices] <- NA

# Verify missing values
#sum(is.na(nhanes$body.weight))  


# Set seed for reproducibility
set.seed(456)  

# Determine the number of missing values (15% of total rows)
num_missing <- round(0.15 * nrow(nhanes))

# Randomly select row indices to introduce missing values
missing_indices <- sample(1:nrow(nhanes), num_missing, replace = FALSE)

# Set selected rows in body.weight to NA
nhanes$cholestrol[missing_indices] <- NA


# Set seed for reproducibility
set.seed(789)  

# Determine the number of missing values (15% of total rows)
num_missing <- round(0.15 * nrow(nhanes))

# Randomly select row indices to introduce missing values
missing_indices <- sample(1:nrow(nhanes), num_missing, replace = FALSE)

# Set selected rows in race to NA
nhanes$race[missing_indices] <- NA

# Verify missing values

#sum(is.na(nhanes$race))  
#sum(is.na(nhanes$cholestrol)) 
#sum(is.na(nhanes$body.weight)) 

# create a copy of nhanes for MICE
nhanes1 <- nhanes
nhanes1$race <- as.factor(nhanes1$race)
nhanes1$cholestrol <- as.numeric(nhanes1$cholestrol)

The National Health and Nutrition Examination Survey (NHANES) is a program conducted by the National Center for Health Statistics (NCHS), a division of the Centers for Disease Control and Prevention (CDC). NHANES has been conducted in various forms since the early 1960s, with the current continuous survey format beginning in 1999. The survey is designed to assess the health and nutritional status of adults and children in the United States through a combination of interviews, physical examinations, and laboratory tests. Data is collected from a nationally representative sample of the U.S. population using a complex, multistage probability sampling design. Participants undergo household interviews followed by health examinations at mobile examination centers (MECs), where trained medical professionals collect biological samples and conduct clinical assessments. The NHANES data provides critical insights into public health trends, helping policymakers, researchers, and healthcare professionals make informed decisions on national health priorities. The purpose of this study is to assess the health and nutritional status of the study participants with metrics such as high blood pressure.

There are 15 features and 7927 observations in this data set. These features are listed below.

  1. obs: Respondent Identification Number
  2. psu: Pseudo-PSU 1,2 psu
  3. stratum: Pseudo-stratum (01 - 49)
  4. stat.weight: Statistical weight (225.93 - 139744.9)
  5. age: Age (years)
  6. sex: 0 = Female, 1 = Male sex
  7. race: Race (1 = White; 2 = Black; 3 = Other)
  8. body.weight: Body Weight (pounds)
  9. height: Standing Height (inches)
  10. avg.sbp: Average Systolic BP (mm/Hg)
  11. avg.dbp: Average Diastolic BP (mm/Hg)
  12. past.smk: Has respondent smoked > 100 cigarettes in life (1 = Yes, 2 = No)
  13. current.smk: Does respondent smoke cigarettes now? (1 = Yes, 2 = No)
  14. cholestrol: Serum Cholesterol (mg/100ml)
  15. hbp: High Blood Pressure (0 if PEPMNK1R <= 140; 1 if PEPMNK1R > 140)

The obs, psu, stratum, and stat.weight variables are not included in this analysis because they do not directly provide health-related information.

The data set has 317 missing values for body weight and 396 missing values for cholesterol.

2 EDA

2.1 Distribution of Individual Features

The distributions of the individual features in the NHANES dataset provide a snapshot of the characteristics of the population in terms of various health and demographic variables. Understanding these distributions is essential for identifying patterns, detecting potential biases, and assessing the range and variability of each feature. This analysis helps in understanding how variables such as age, body weight, blood pressure, and lifestyle factors are spread across the dataset, which is crucial for subsequent statistical modeling and interpretation.

# Convert variables to factors with proper labels
nhanes$sex <- factor(nhanes$sex, levels = c(0, 1), labels = c("Female", "Male"))
nhanes$race <- factor(nhanes$race, levels = c(1, 2, 3), labels = c("White", "Black", "Other"))
nhanes$past.smk <- factor(nhanes$past.smk, levels = c(1, 2), labels = c("Yes", "No"))
nhanes$current.smk <- factor(nhanes$current.smk, levels = c(1, 2), labels = c("Yes", "No"))

# Create frequency tables
sex_count <- table(nhanes$sex)

race_count <- table(nhanes$race)

past.smk_count <- table(nhanes$past.smk)

current.smk_count <- table(nhanes$current.smk)


par(mfrow = c(2, 2))

# Plot the bar charts
barplot(height = sex_count, col = "steelblue", main = "Sex", xlab = "Sex", ylab = "Count")

barplot(height = race_count, col = "steelblue", main = "Race", xlab = "Race", ylab = "Count")

barplot(height = past.smk_count, col = "steelblue", main = "Past Smoker", xlab = "Past Smoker", ylab = "Count")

barplot(height = current.smk_count, col = "steelblue", main = "Current Smoker", xlab = "Current Smoker", ylab = "Count")

There are slightly more male observations than female observation in this data set. The majority of participants are white, followed by black. Most participants were past smokers. In fact, there is an imbalance for past smoker. About half of current participants are current smokers.

par(mfrow = c(2, 2))  

# Histogram for Cholesterol
hist(nhanes$cholestrol, 
     main = "Histogram of Cholesterol", 
     xlab = "Cholesterol", 
     col = "steelblue", 
     border = "black", 
     breaks = 20)  # Control number of bins
    
     
# Histogram for Systolic BP
hist(nhanes$avg.sbp, 
     main = "Histogram of Systolic BP", 
     xlab = "Systolic BP", 
     col = "steelblue", 
     border = "black", 
     breaks = 20)
    

# Histogram for Body Weight
hist(nhanes$body.weight, 
     main = "Histogram of Body Weight", 
     xlab = "Body Weight", 
     col = "steelblue", 
     border = "black", 
     breaks = 20)
  
     
# Histogram for Height
hist(nhanes$body.weight, 
     main = "Histogram of Height", 
     xlab = "Height", 
     col = "steelblue", 
     border = "black", 
     breaks = 20)   

Most participants do not have high blood pressure. The age of participants varies from about 20 to about 90. The height distribution is roughly symmetric. The body weight distribution is skewed right due to some high outliers. The high outliers may indicate obesity.

par(mfrow = c(2,2))

hist(nhanes$avg.sbp, col = "steelblue", 
     main = "Systolic BP",
     xlab = "Systolic BP", ylab = "Frequency",
     breaks = 30)

hist(nhanes$avg.dbp, col = "steelblue", 
     main = "Diastolic BP",
     xlab = "Diastolic BP", ylab = "Frequency",
     breaks = 30)

hist(nhanes$cholestrol, col = "steelblue", 
     main = "Cholesterol",
     xlab = "Cholesterol", ylab = "Frequency",
     breaks = 30)

Systolic BP and cholesterol have skewed right distributions. Diastolic BP is roughly symmetric. Of the 7927 participants, 1964 are obese.

2.2 Relationship between Features

Examining relationships between variables using scatterplots and a correlation matrix provides a comprehensive approach to understanding associations. Scatterplots visually reveal how two continuous variables interact, allowing us to identify trends, correlations, and potential outliers. Meanwhile, a correlation matrix quantifies the strength and direction of relationships between multiple variables at once, helping to pinpoint which pairs are strongly correlated. Together, scatterplots and a correlation matrix offer valuable insights into the underlying patterns in the data, guiding further statistical analysis and model selection.

# Set seed for reproducibility
set.seed(123)

# Sample 500 rows (adjust the number as needed)
sample_indices <- sample(nrow(nhanes), size = 500, replace = TRUE)
sampled_data <- nhanes[sample_indices, c("age", "body.weight", "height", "avg.sbp", "avg.dbp", "cholestrol")]

# Create a pairwise scatterplot matrix with the sampled data
pairs(sampled_data, 
      main = "Pairwise Scatterplots (Bootstrapped Sample)",
      pch = 19,           # Shape of the points (circle)
      col = rgb(0, 0, 1, 0.5)  # Color with transparency (blue)
)

nhanes_subset <- nhanes[, c("age", "body.weight", "height", "avg.sbp", "avg.dbp", "cholestrol")]

# Compute the correlation matrix
cor_matrix <- cor(nhanes_subset, use = "pairwise.complete.obs")

# Print the correlation matrix
print(cor_matrix)
                    age body.weight      height    avg.sbp   avg.dbp cholestrol
age          1.00000000 -0.01377966 -0.08538971 0.55641362 0.1143690  0.2738272
body.weight -0.01377966  1.00000000  0.45314709 0.13693751 0.2900425  0.1032280
height      -0.08538971  0.45314709  1.00000000 0.01379185 0.2060106 -0.0631896
avg.sbp      0.55641362  0.13693751  0.01379185 1.00000000 0.5213667  0.2306877
avg.dbp      0.11436905  0.29004249  0.20601064 0.52136671 1.0000000  0.1706567
cholestrol   0.27382718  0.10322800 -0.06318960 0.23068766 0.1706567  1.0000000
corrplot(cor_matrix, method = "color", type = "upper", tl.col = "black", tl.cex = 0.8)

The most highly correlate features are age and average systolic blood pressure, body weight and height, average systolic blood pressure and average diastolic blood pressure, age and cholesterol, body weight and cholesterol, and body weigh and average diastolic blood pressure. The pairwise relationships are displayed with individual scatter plots.

# Set seed for reproducibility
set.seed(123)

# Bootstrap a smaller sample (adjust size as needed)
sample_indices <- sample(nrow(nhanes), size = 500, replace = TRUE)
sampled_data <- nhanes[sample_indices, ]

# Set up a 2x3 plotting grid
par(mfrow = c(2, 3))

# Scatterplot for Age and Avg. Systolic Blood Pressure
plot(sampled_data$age, sampled_data$avg.sbp, main = "Age vs Avg. Systolic BP", 
     xlab = "Age", ylab = "Avg. Systolic BP", col = "blue", pch = 19)

# Scatterplot for Body Weight and Height
plot(sampled_data$body.weight, sampled_data$height, main = "Body Weight vs Height", 
     xlab = "Body Weight", ylab = "Height", col = "red", pch = 19)

# Scatterplot for Avg. Systolic Blood Pressure and Avg. Diastolic Blood Pressure
plot(sampled_data$avg.sbp, sampled_data$avg.dbp, main = "Avg. Systolic vs Avg. Diastolic BP", 
     xlab = "Avg. Systolic BP", ylab = "Avg. Diastolic BP", col = "forestgreen", pch = 19)

# Scatterplot for Age and Cholesterol
plot(sampled_data$age, sampled_data$cholestrol, main = "Age vs Cholesterol", 
     xlab = "Age", ylab = "Cholesterol", col = "purple", pch = 19)

# Scatterplot for Avg. Systolic BP and Cholesterol
plot(sampled_data$avg.sbp, sampled_data$cholestrol, main = "Avg Systolic BP vs Cholesterol", 
     xlab = "Cholesterol", ylab = "Avg.SBP", col = "gold", pch = 19)

# Scatterplot for Body Weight and Avg. Diastolic Blood Pressure
plot(sampled_data$body.weight, sampled_data$avg.dbp, main = "Body Weight vs Avg. Diastolic BP", 
     xlab = "Body Weight", ylab = "Avg. Diastolic BP", col = "maroon", pch = 19)

Six of the most correlated pairwise relationship are visualized in scatter plots. All pairwise relationships are linear with no apparent clustering.

# Set up a 2x2 plotting grid
par(mfrow = c(2, 2))

# Create a contingency table
sex_hbp_table <- table(nhanes$sex, nhanes$hbp)

# Generate mosaic plot
mosaicplot(sex_hbp_table, 
           col = c("gold", "royalblue"), 
           main = "Sex and High Blood Pressure",
           xlab = "Sex Status", 
           ylab = "HBP Status",
           border = "black")


# Create a contingency table
race_hbp_table <- table(nhanes$race, nhanes$hbp)

# Generate mosaic plot
mosaicplot(race_hbp_table, 
           col = c("gold", "royalblue"), 
           main = "Race and High Blood Pressure",
           xlab = "Race", 
           ylab = "HBP Status",
           border = "black")


# Create a contingency table
current.smk_hbp_table <- table(nhanes$current.smk, nhanes$hbp)

# Generate mosaic plot
mosaicplot(current.smk_hbp_table, 
           col = c("gold", "royalblue"), 
           main = "Current Smoking Status and HBP",
           xlab = "Current Smoking Status", 
           ylab = "HBP Status",
           border = "black")


# Create a contingency table
past.smk_hbp_table <- table(nhanes$past.smk, nhanes$hbp)

# Generate mosaic plot
mosaicplot(past.smk_hbp_table, 
           col = c("gold", "royalblue"), 
           main = "Past Smoking Status and HBP",
           xlab = "Past Smoking Status", 
           ylab = "HBP Status",
           border = "black")

Relationship between high blood pressure and race, sex, and current smoking status can be visualized in the mosaic plots. There appears to be an association between high current smoking and race with high blood pressure. A less pronounced relationship can been seen between sex and race with high blood pressure.

# Set up a 2x2 plotting grid
par(mfrow = c(1, 2))


# Create a contingency table
current.smk_race_table <- table(nhanes$race, nhanes$current.smk)

# Generate mosaic plot
mosaicplot(current.smk_race_table, 
           col = c("gold", "royalblue"), 
           main = "Race and Current Smoke Status",
           xlab = "Race", 
           ylab = "Current Smoke",
           border = "black")


# Create a contingency table
current.smk_sex_table <- table(nhanes$sex, nhanes$current.smk)

# Generate mosaic plot
mosaicplot(current.smk_sex_table, 
           col = c("gold", "royalblue"), 
           main = "Sex and Current Smoke Status",
           xlab = "Sex", 
           ylab = "Current Smoke",
           border = "black")

# Create a contingency table
past.smk_race_table <- table(nhanes$race, nhanes$past.smk)

An association exists between race and current smoking. Black individuals tend to smoke more than white or other individuals. An association exists between sex and current smoke status. Female tend to smoke currently slight more than males in this population.

3 MISSING VALUES

3.1 Imputation for Categorical Features

The distribution of missing values for is examined to determine any patterns. This step is done to ensure that value are missing at random.

# Set up a 2x2 plotting grid
par(mfrow = c(2, 2))

#sum(is.na(nhanes$race))

#mean(is.na(nhanes$race))

#table(nhanes$race, useNA = "ifany")


#ggplot(nhanes, aes(x = factor(race))) +
  #geom_bar(fill = "blue") +
  #labs(title = "Distribution of Race (Before Imputation)", x = "Race", y = "Count") +
  #theme_minimal()


aggr(nhanes, col = c("navyblue", "red"), numbers = TRUE, sortVars = TRUE, 
     labels = names(nhanes), cex.axis = 0.7, gap = 2, 
     ylab = c("Missing Data Overview", "Pattern of Missing Data"))


 Variables sorted by number of missings: 
    Variable     Count
        race 0.1499937
  cholestrol 0.1499937
 body.weight 0.1400278
           X 0.0000000
         obs 0.0000000
         psu 0.0000000
     stratum 0.0000000
 stat.weight 0.0000000
         age 0.0000000
         sex 0.0000000
      height 0.0000000
     avg.sbp 0.0000000
     avg.dbp 0.0000000
    past.smk 0.0000000
 current.smk 0.0000000
         hbp 0.0000000

A pattern of missing values exists between body weight, BMI, and obese. This pattern can be explained by the fact that body weight is used to calculate BMI, which is then used to classify observations for obese. However, the pattern of missing value appears to be random for the other features.

No pattern of missing values exists between race and other features, In turn, kNN imputation is used for missing values of race.

sum(is.na(nhanes$race))
[1] 1189
table(nhanes$race, useNA = "ifany")  # Check distribution including NAs

White Black Other  <NA> 
 4693  1858   187  1189 
# Perform KNN Imputation (single imputation)
nhanes_imputed <- kNN(nhanes, variable = "race", k = 3)

# Keep only the original columns (without extra columns added by kNN)
nhanes_imputed <- nhanes_imputed[, names(nhanes)]



# COMPARE before and after imputation distribution

par(mfrow = c(1,2))  # Split plotting area into two

# Convert race to a factor for better visualization
nhanes$race <- as.factor(nhanes$race)
nhanes_imputed$race <- as.factor(nhanes_imputed$race)

# Create a new column to indicate whether the data is original or imputed
nhanes$source <- "Before Imputation"
nhanes_imputed$source <- "After Imputation"

# Combine both datasets
nhanes_compare <- bind_rows(nhanes, nhanes_imputed)

# Plot the distribution before and after imputation
ggplot(nhanes_compare, aes(x = race, fill = source)) +
  geom_bar(position = "dodge") +
  labs(title = "Distribution of Race Before and After Imputation",
       x = "Race",
       y = "Count",
       fill = "Dataset") +
  theme_minimal()

imputed_nhanes <- nhanes_imputed

The overall distribution of race after imputation changed slightly after imputation.

3.2 Regression-Based Imputation for Numerical Feature

The pattern of missing values for body weight and cholesterol is examined for any potential patterns.

#vis_miss(nhanes[, c("body.weight", "cholestrol")])


# Set smaller font size for the plot
par(cex = 0.4)  # Adjust this value for desired font size

# Generate the missing data pattern plot

md.pattern(imputed_nhanes)

     X obs psu stratum stat.weight age sex race height avg.sbp avg.dbp past.smk
5792 1   1   1       1           1   1   1    1      1       1       1        1
1025 1   1   1       1           1   1   1    1      1       1       1        1
946  1   1   1       1           1   1   1    1      1       1       1        1
164  1   1   1       1           1   1   1    1      1       1       1        1
     0   0   0       0           0   0   0    0      0       0       0        0
     current.smk hbp source body.weight cholestrol     
5792           1   1      1           1          1    0
1025           1   1      1           1          0    1
946            1   1      1           0          1    1
164            1   1      1           0          0    2
               0   0      0        1110       1189 2299
# Reset graphics settings (optional)
par(cex = 1)

A pattern of missing values exists between body weight, BMI, and obese because

Body weight is correlated with height, average diastolic blood pressure, and sex. In turn, the missing body weight values will be imputed using a regression model using said explanatory features.

# Subset nhanes to contain only complete observations for selected variables
complete_nhanes <- imputed_nhanes[complete.cases(imputed_nhanes[, c("body.weight", "height", "avg.dbp", "sex")]), ]

# Fit the linear regression model
model_body.weight <- lm(body.weight ~ height + avg.dbp + sex, data = complete_nhanes)

# View the model summary
summary(model_body.weight)

Call:
lm(formula = body.weight ~ height + avg.dbp + sex, data = complete_nhanes)

Residuals:
    Min      1Q  Median      3Q     Max 
-86.131 -22.276  -4.259  17.073 269.863 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -207.90214    9.27828 -22.407  < 2e-16 ***
height         4.86582    0.14277  34.080  < 2e-16 ***
avg.dbp        0.76728    0.03913  19.606  < 2e-16 ***
sexMale       -7.00000    1.07917  -6.486  9.4e-11 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 33.52 on 6813 degrees of freedom
Multiple R-squared:  0.2495,    Adjusted R-squared:  0.2491 
F-statistic: 754.9 on 3 and 6813 DF,  p-value: < 2.2e-16
# Plot the residuals
plot(model_body.weight$residuals)

The residual plot shows no patterns. In turn, the model fit the data well and the assumption of constant variance of residuals is satisfied.

# Create a copy of the dataset to work with
#imputed_nhanes <- complete_nhanes

colSums(is.na(imputed_nhanes))
          X         obs         psu     stratum stat.weight         age 
          0           0           0           0           0           0 
        sex        race body.weight      height     avg.sbp     avg.dbp 
          0           0        1110           0           0           0 
   past.smk current.smk  cholestrol         hbp      source 
          0           0        1189           0           0 
# Identify the rows with missing values for body.weight
missing_body_weight <- is.na(imputed_nhanes$body.weight)

# Use the model to predict the missing values
imputed_nhanes$body.weight[missing_body_weight] <- predict(model_body.weight, 
                                                           newdata = imputed_nhanes[missing_body_weight, c("height", "avg.dbp", "sex", "age")])

# Check the dataset
#head(imputed_nhanes)

# Create a plot to overlay the density curves
ggplot() +
  geom_density(data = complete_nhanes, aes(x = body.weight), fill = "blue", alpha = 0.2, na.rm = TRUE) + 
  geom_density(data = imputed_nhanes, aes(x = body.weight), fill = "red", alpha = 0.2, na.rm = TRUE) +
  labs(title = "Density Plot of Body Weight: Before and After Imputation",
       x = "Body Weight", y = "Density") +
  theme_minimal() +
  scale_fill_manual(name = "Body Weight", values = c("blue" = "blue", "red" = "red"), 
                    labels = c("Before Imputation", "After Imputation"))

The distribution before and after imputation is similar. In turn, the imputation did not change the distribution of body weight.

The features that are most correlated to cholesterol are age, average systolic blood pressure, and body weight. In turn, the missing values of cholesterol will be imputed with a model using these features.

# Subset nhanes to include only rows with complete cases for cholestrol, age, avg.sbp, and BMI
complete_nhanes <- imputed_nhanes[!is.na(imputed_nhanes$cholestrol) & !is.na(imputed_nhanes$age) & 
                           !is.na(imputed_nhanes$avg.sbp) & !is.na(imputed_nhanes$body.weight), ]

complete_nhanes <- imputed_nhanes[complete.cases(imputed_nhanes[, c("cholestrol", "age", "avg.sbp", "body.weight")]), ]

# Fit the linear regression model to predict cholestrol
model_cholestrol <- lm(cholestrol ~ age + avg.sbp + body.weight, data = complete_nhanes)
summary(model_cholestrol)

Call:
lm(formula = cholestrol ~ age + avg.sbp + body.weight, data = complete_nhanes)

Residuals:
    Min      1Q  Median      3Q     Max 
-162.16  -28.18   -3.08   24.08  498.54 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 135.10667    3.96327  34.090  < 2e-16 ***
age           0.54613    0.03465  15.762  < 2e-16 ***
avg.sbp       0.21908    0.03267   6.707 2.15e-11 ***
body.weight   0.10341    0.01437   7.198 6.76e-13 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 42.59 on 6734 degrees of freedom
Multiple R-squared:  0.09077,   Adjusted R-squared:  0.09037 
F-statistic: 224.1 on 3 and 6734 DF,  p-value: < 2.2e-16
# View the model summary
summary(model_cholestrol)

Call:
lm(formula = cholestrol ~ age + avg.sbp + body.weight, data = complete_nhanes)

Residuals:
    Min      1Q  Median      3Q     Max 
-162.16  -28.18   -3.08   24.08  498.54 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 135.10667    3.96327  34.090  < 2e-16 ***
age           0.54613    0.03465  15.762  < 2e-16 ***
avg.sbp       0.21908    0.03267   6.707 2.15e-11 ***
body.weight   0.10341    0.01437   7.198 6.76e-13 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 42.59 on 6734 degrees of freedom
Multiple R-squared:  0.09077,   Adjusted R-squared:  0.09037 
F-statistic: 224.1 on 3 and 6734 DF,  p-value: < 2.2e-16
# Create a copy of nhanes for imputation
#imputed_nhanes <- nhanes

# Identify rows with missing cholestrol values
missing_cholestrol <- is.na(imputed_nhanes$cholestrol)

# Use the model to predict the missing cholestrol values
imputed_nhanes$cholestrol[missing_cholestrol] <- predict(model_cholestrol, 
                                                          newdata = imputed_nhanes[missing_cholestrol, c("age", "avg.sbp", "body.weight")])

All explanatory features are significant.

# Get residuals from the model for complete_nhanes
residuals_cholestrol <- residuals(model_cholestrol)

# Predicted values for complete_nhanes
predicted_cholestrol <- predict(model_cholestrol, newdata = complete_nhanes)

# Create a data frame for residuals and predicted values
residuals_data <- data.frame(
  predicted_cholestrol = predicted_cholestrol,
  residuals_cholestrol = residuals_cholestrol
)

# Create the residual plot
library(ggplot2)
ggplot(residuals_data, aes(x = predicted_cholestrol, y = residuals_cholestrol)) +
  geom_point(color = "blue") +  # Plot the residuals
  geom_hline(yintercept = 0, linetype = "dashed", color = "red") +  # Add a horizontal line at 0
  labs(title = "Residual Plot for Cholestrol Prediction",
       x = "Predicted Cholestrol",
       y = "Residuals") +
  theme_minimal()

# Create the overlay density plot
ggplot() +
  geom_density(data = nhanes, aes(x = cholestrol), fill = "blue", alpha = 0.2, na.rm = TRUE) +  # Before imputation
  geom_density(data = imputed_nhanes, aes(x = cholestrol), fill = "red", alpha = 0.2, na.rm = TRUE) +  # After imputation
  labs(title = "Density Plot of Cholestrol: Before and After Imputation",
       x = "Cholestrol", y = "Density") +
  theme_minimal() +
  scale_fill_manual(name = "Cholestrol", values = c("blue" = "blue", "red" = "red"), 
                    labels = c("Before Imputation", "After Imputation"))

The distribution of cholesterol did not change significantly after imputation. As well, the residual plot does not display any patterns. In turn, the model fit the data well and the assumption of constant variance of residuals is satisfied.

3.3 Feature Engineer

In turn a feature which calculates BMI is created to determine observations are classified as obese. According to the CDC (https://www.cdc.gov/nccdphp/dnpao/data-trends-maps/help/npao_dtm/definitions.html), Adult obesity is defined as body mass index (BMI) ≥ 30.0. Another feature named obese, based on BMI, is created, where 0 = no and 1 = yes.

# Create BMI feature
imputed_nhanes$BMI <- (imputed_nhanes$body.weight * 703) / (imputed_nhanes$height * imputed_nhanes$height)
# Round the BMI to the nearest ones place
imputed_nhanes$BMI <- round(imputed_nhanes$BMI, 0)

# Create the obese feature based on BMI
imputed_nhanes$obese <- ifelse(imputed_nhanes$BMI >= 30, 1, 0)
par(mfrow = c(1, 2))

# Histogram for BMI

hist(imputed_nhanes$BMI, 
     main = "Histogram of BMI", 
     xlab = "BMI", 
     col = "steelblue", 
     border = "black", 
     breaks = 20)

# bar chart for obese
nhanes$obese <- factor(imputed_nhanes$obese, levels = c(0, 1), labels = c("No", "Yes"))

obese_count <- table(imputed_nhanes$obese)

barplot(height = obese_count, col = "steelblue", main = "Obesity", xlab = "Obesity", ylab = "Count")

# Set up a 2x2 plotting grid
par(mfrow = c(2, 2))


# Create a contingency table
obese_hbp_table <- table(imputed_nhanes$obese, imputed_nhanes$hbp)

# Generate mosaic plot
mosaicplot(obese_hbp_table, 
           col = c("gold", "royalblue"), 
           main = "Obesity and High Blood Pressure",
           xlab = "Obesity Status", 
           ylab = "HBP Status",
           border = "black")

# Create a contingency table
obese_sex_table <- table(imputed_nhanes$sex, imputed_nhanes$obese)

# Generate mosaic plot
mosaicplot(obese_sex_table, 
           col = c("gold", "royalblue"), 
           main = "Sex and Obesity",
           xlab = "Obesity Status", 
           ylab = "Sex",
           border = "black")


# Create a contingency table
obese_race_table <- table(imputed_nhanes$race, imputed_nhanes$obese)

# Generate mosaic plot
mosaicplot(obese_race_table, 
           col = c("gold", "royalblue"), 
           main = "Race and Obesity",
           xlab = "Obesity Status", 
           ylab = "Race",
           border = "black")


# Create a contingency table
obese_smoke_table <- table(imputed_nhanes$current.smk, imputed_nhanes$obese)

# Generate mosaic plot
mosaicplot(obese_smoke_table, 
           col = c("gold", "royalblue"), 
           main = "Current Smoking Status and Obesity",
           xlab = "Obesity Status", 
           ylab = "Current Smoking Status",
           border = "black")

3.4 Multiple Imputation

Multiple Imputation by Chained Equations (MICE) is a powerful technique used to handle missing data by creating multiple plausible imputations for each missing value based on relationships in the observed data. For body weight, cholesterol, and race, MICE can be applied to generate accurate imputations, accounting for correlations between these variables and other features in the dataset. This method allows for more reliable statistical analyses by reducing bias and preserving variability in the imputed values, making it especially useful for handling missing data in incoming new data sets.

nhanes1$race <- as.factor(nhanes1$race)  
nhanes1$cholestrol <- as.numeric(nhanes1$cholestrol)
nhanes1$body.weight <- as.numeric(nhanes1$body.weight)

nhanes2 <- nhanes1


# 1 initialization

init <- mice(nhanes2, maxit=0)
print(init$method)
          X         obs         psu     stratum stat.weight         age 
         ""          ""          ""          ""          ""          "" 
        sex        race body.weight      height     avg.sbp     avg.dbp 
         ""   "polyreg"       "pmm"          ""          ""          "" 
   past.smk current.smk  cholestrol         hbp 
         ""          ""       "pmm"          "" 
# 2 imputation models

imp <- mice(nhanes2, method = c("", "", "", "", "", "", "", "polyreg", "pmm", "", "", "", "", "", "pmm", ""), 
            maxit=10,
            m=1,
            seed = 123,
            print=F)

complete(imp, action = 1)[1:10,]
    X obs psu stratum stat.weight age sex race body.weight height avg.sbp
1   3   9   2      43    19451.83  48   0    1       149.7   61.8     131
2   5  11   2      40     1245.52  48   1    1       155.3   66.2     120
3   6  19   1      35     3860.97  44   1    2       189.6   70.2     133
4   7  34   1      13     5031.70  42   0    2       125.8   62.6     100
5  10  48   1      24    26919.29  56   0    1       239.9   67.6     128
6  12  51   1      28     2730.56  44   1    1       317.9   71.1     130
7  15  55   2      44      804.03  48   1    1       245.6   68.0     155
8  19  70   1       9     2629.39  63   1    2       202.9   67.8     137
9  20  71   1      43     1147.32  37   0    1       151.7   61.7     128
10 22  73   1      29     3991.81  42   1    3       205.0   68.4     148
   avg.dbp past.smk current.smk cholestrol hbp
1       73        1           2        236   0
2       70        1           2        260   0
3       85        1           1        187   0
4       67        1           1        216   0
5       73        1           2        156   0
6       86        1           1        162   0
7       91        1           1        212   1
8       68        1           1        186   0
9       70        1           1        209   0
10      83        1           1        267   1
# 3 multiple (iterative) imputations

imp5 <- mice(nhanes2, method = "pmm", m = 5, maxit = 10, seed = 123, print=F)
plot(imp5)

Multiple Imputation by Chained Equations (MICE) will be applied to handle missing values in the body weight variable. To assess the quality of the imputations, the Mean Squared Error (MSE) will be calculated and compared the MSE of the single imputation conducted for body weight earlier in this analysis, providing a measure of the imputation accuracy.

model5_bodyweight <- with(imp5, lm(body.weight ~ height + avg.dbp + sex))
summary.stats = summary(model5_bodyweight)


summary(pool(model5_bodyweight))
         term     estimate  std.error  statistic        df       p.value
1 (Intercept) -205.7045484 8.74935482 -23.510825 2473.3145 1.747163e-110
2      height    4.8466598 0.13536721  35.803796 1634.9416 8.934110e-208
3     avg.dbp    0.7523449 0.03941721  19.086712  157.8342  7.501512e-43
4         sex   -6.6078962 1.06330598  -6.214482  276.0479  1.892089e-09
beta = summary.stats$estimate[seq(1,15,by=3)]  # explicit vector: c(1,4,7,10,13)
beta.var = (summary.stats$std.error[seq(1,15,by=3)])^2 
Q = mean(beta)
U = mean(beta.var)
B = var(beta)
T = U + (6/5)*B
pool.se = sqrt(T)
cbind(pool.se.intercept = pool.se)
     pool.se.intercept
[1,]          123.2177

The model can be used to predict body weight for incoming new data.

The MSE for single body weight imputation used earlier in this analysis is MSE = (RSE)^2 = (33.52)^2 = 1123.6 which is substantially higher than MSE when using MICE. In turn, MICE is recommended in this analysis.

Multiple Imputation by Chained Equations (MICE) will be applied to handle missing values in the cholesterol variable. To assess the quality of the imputations, the Mean Squared Error (MSE) will be calculated and compared the MSE of the single imputation conducted for cholesterol earlier in this analysis, providing a measure of the imputation accuracy.

model5_chol <- with(imp5, lm(cholestrol ~ age + avg.sbp))
summary.stats = summary(model5_chol)


summary(pool(model5_chol))
         term    estimate  std.error statistic        df      p.value
1 (Intercept) 148.6912121 3.65965489 40.629845  74.24158 1.827860e-52
2         age   0.5233278 0.03407744 15.357017 236.74859 2.129193e-37
3     avg.sbp   0.2585339 0.03425598  7.547118  63.62530 2.094322e-10
beta = summary.stats$estimate[seq(1,15,by=3)]  # explicit vector: c(1,4,7,10,13)
beta.var = (summary.stats$std.error[seq(1,15,by=3)])^2 
Q = mean(beta)
U = mean(beta.var)
B = var(beta)
T = U + (6/5)*B
pool.se = sqrt(T)
cbind(pool.se.intercept = pool.se)
     pool.se.intercept
[1,]          3.659655

The model can be used to predict cholesterol for incoming new data.

The Mean Squared Error (MSE) from the Residual Standard Error (RSE) given in the model output from the cholesterol single imputation method used early in this analysis is MSE = (42.59)^2 = 1813.9. Thus, the Mean Squared Error (MSE) = 1813.9.

The MSE using MICE is 3.66 which is substantially less. Therefore, MICE is recommended in the analysis of this data set.

For the missing values in the race variable, Multiple Imputation by Chained Equations (MICE) will be applied using multinomial logistic regression. This method will account for the categorical nature of the race variable, allowing for appropriate imputation based on the relationships with other features in the dataset.

model_race <- with(imp5, multinom(race ~ avg.dbp + avg.sbp + cholestrol + hbp))
# weights:  18 (10 variable)
initial  value 8708.699612 
iter  10 value 5523.235718
final  value 5495.930272 
converged
# weights:  18 (10 variable)
initial  value 8708.699612 
iter  10 value 5555.595871
final  value 5522.606318 
converged
# weights:  18 (10 variable)
initial  value 8708.699612 
iter  10 value 5555.946819
final  value 5529.649391 
converged
# weights:  18 (10 variable)
initial  value 8708.699612 
iter  10 value 5560.173296
final  value 5529.439901 
converged
# weights:  18 (10 variable)
initial  value 8708.699612 
iter  10 value 5542.550592
final  value 5510.127472 
converged
# Pool the results from the imputed datasets
pooled_race <- pool(model_race)

# Get a summary of the pooled results
summary(pooled_race)
          term     estimate    std.error  statistic         df      p.value
1  (Intercept) -1.277932787 0.3325448882 -3.8428881   49.56081 3.468517e-04
2      avg.dbp  0.030728414 0.0030785469  9.9814670  343.89941 9.007490e-21
3      avg.sbp -0.009072042 0.0025775079 -3.5196952  252.21602 5.124609e-04
4   cholestrol -0.004119817 0.0007738102 -5.3240668   28.44201 1.091924e-05
5          hbp  0.148914062 0.1127433356  1.3208236  101.89054 1.895191e-01
6  (Intercept) -2.575346290 0.9149529588 -2.8147308   40.55536 7.498473e-03
7      avg.dbp  0.019877690 0.0080743228  2.4618399 3279.39165 1.387374e-02
8      avg.sbp -0.007708776 0.0071499482 -1.0781583  208.24247 2.822104e-01
9   cholestrol -0.005554712 0.0020814684 -2.6686508   36.04555 1.134283e-02
10         hbp -0.151093362 0.3197542727 -0.4725296  109.96948 6.374852e-01

The model can be used to predict race for incoming new data.

4 FEATURE TRANSFORMING

Based on the distribution patterns observed in the NHANES data set, several feature engineering tasks can be performed to enhance the data’s usability for modeling. First, for age, which is roughly uniform, no major transformation is needed, though we may consider binning it into age groups if it helps with interpretability. For weight and systolic blood pressure (BP), which are both skewed right, a log transformation could be applied to reduce skewness and make these variables more normally distributed, improving model performance. Similarly, for cholesterol, which is slightly skewed right, a log transformation may also be useful. For diastolic BP, which is roughly symmetric, no transformation is necessary, but checking for outliers could be helpful. These transformations aim to stabilize variance, reduce the impact of extreme values, and create features that are more suitable for modeling with techniques like linear regression or other statistical methods.

#Log transformation to reduce skew

imputed_nhanes$log_body.weight <- log(imputed_nhanes$body.weight)
imputed_nhanes$log_avg.sbp <- log(imputed_nhanes$avg.sbp)
imputed_nhanes$log_cholestrol <- log(imputed_nhanes$cholestrol)
imputed_nhanes$log_BMI <- log(imputed_nhanes$BMI)




# Set up the plotting area to show 2x2 grid of histograms
par(mfrow = c(2, 2))  # This will create a 2x2 grid of plots

# Histogram for Log-Cholesterol
hist(imputed_nhanes$log_cholestrol, 
     main = "Histogram of Log(Cholesterol)", 
     xlab = "Log(Cholesterol)", 
     col = "steelblue", 
     border = "black", 
     breaks = 20)  
    
# Histogram for Log-Body Weight
hist(imputed_nhanes$log_body.weight, 
     main = "Histogram of Log(Body Weight)", 
     xlab = "Log(Body Weight)", 
     col = "steelblue", 
     border = "black", 
     breaks = 20) 


# Histogram for Log-Systolic BP
hist(imputed_nhanes$log_avg.sbp, 
     main = "Histogram of Log(Systolic BP)", 
     xlab = "Log(Systolic BP)", 
     col = "steelblue", 
     border = "black", 
     breaks = 20)
   

# Histogram for Log-BMI
hist(imputed_nhanes$log_BMI, 
     main = "Histogram of Log(BMI)", 
     xlab = "Log(BMI)", 
     col = "steelblue", 
     border = "black", 
     breaks = 20)

The distributions of these previously skewed features are more symmetric after log transformations.

5 STANDARDIZING

Standardizing features ensures that variables with different scales, such as age, BMI, height, and cholesterol, contribute equally to the analysis. Without standardization, variables with larger ranges could dominate model performance, leading to biased results. By standardizing these features, we improve model accuracy, enable better comparison between variables, and enhance the efficiency of machine learning algorithms, particularly in models that rely on distance-based or gradient-based methods.

# Specify the columns to be standardized
columns_to_standardize <- c("age", "BMI", "height", "body.weight", "cholestrol", "avg.dbp", "avg.sbp")

# Standardize the selected columns
imputed_nhanes[columns_to_standardize] <- scale(imputed_nhanes[columns_to_standardize])

6 FEATURE SELECTION

Feature selection is a critical step in model building, as it helps improve model performance by identifying the most relevant predictors and reducing overfitting. In this analysis, both model-based selection and regularization-based selection techniques will be used to assess and retain the most influential features for accurate prediction, ensuring a more efficient and interpretable model.

A full model is created to predict cholesterol.

#sapply(imputed_nhanes, function(x) length(unique(x)))

#head(imputed_nhanes, 25)


#head(imputed_nhanes)


model_full <- lm(cholestrol ~ age + race + BMI + height + body.weight + 
                  hbp + avg.dbp + avg.sbp + obese, 
                  data = imputed_nhanes)

# Summary of the full model
summary(model_full)

Call:
lm(formula = cholestrol ~ age + race + BMI + height + body.weight + 
    hbp + avg.dbp + avg.sbp + obese, data = imputed_nhanes)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.7355 -0.5662 -0.0433  0.4439 11.9436 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.04277    0.01748   2.447 0.014423 *  
age          0.24974    0.01325  18.847  < 2e-16 ***
raceBlack   -0.06770    0.02435  -2.781 0.005436 ** 
raceOther   -0.20562    0.06724  -3.058 0.002237 ** 
BMI         -0.12183    0.08249  -1.477 0.139735    
height      -0.18964    0.04910  -3.862 0.000113 ***
body.weight  0.26560    0.09330   2.847 0.004426 ** 
hbp         -0.06604    0.04197  -1.574 0.115618    
avg.dbp      0.11648    0.01327   8.774  < 2e-16 ***
avg.sbp      0.05361    0.02064   2.597 0.009419 ** 
obese       -0.02242    0.03806  -0.589 0.555846    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.9366 on 7916 degrees of freedom
Multiple R-squared:  0.1238,    Adjusted R-squared:  0.1227 
F-statistic: 111.9 on 10 and 7916 DF,  p-value: < 2.2e-16

Stepwise model selection is conducted to retain significant predictors. Selection processes based on AIC and BIC are conducted.

# Stepwise model selection based on AIC
model_stepwise_AIC <- step(model_full, direction = "both", trace = 0)

# Stepwise model selection based on BIC
model_stepwise_BIC <- step(model_full, direction = "both", k = log(nrow(imputed_nhanes)), trace = 0)

# Display the summary of the selected model
summary(model_stepwise_AIC)

Call:
lm(formula = cholestrol ~ age + race + BMI + height + body.weight + 
    hbp + avg.dbp + avg.sbp, data = imputed_nhanes)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.7302 -0.5629 -0.0437  0.4447 11.9404 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.03788    0.01538   2.462 0.013820 *  
age          0.24998    0.01324  18.875  < 2e-16 ***
raceBlack   -0.06795    0.02434  -2.792 0.005257 ** 
raceOther   -0.20591    0.06724  -3.062 0.002203 ** 
BMI         -0.12726    0.08197  -1.553 0.120563    
height      -0.18849    0.04906  -3.842 0.000123 ***
body.weight  0.26375    0.09324   2.829 0.004685 ** 
hbp         -0.06660    0.04195  -1.588 0.112433    
avg.dbp      0.11652    0.01327   8.778  < 2e-16 ***
avg.sbp      0.05375    0.02064   2.604 0.009223 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.9366 on 7917 degrees of freedom
Multiple R-squared:  0.1238,    Adjusted R-squared:  0.1228 
F-statistic: 124.3 on 9 and 7917 DF,  p-value: < 2.2e-16
summary(model_stepwise_BIC)

Call:
lm(formula = cholestrol ~ age + height + body.weight + avg.dbp, 
    data = imputed_nhanes)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.6387 -0.5588 -0.0418  0.4475 11.9160 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -5.892e-16  1.053e-02   0.000        1    
age          2.714e-01  1.067e-02  25.433   <2e-16 ***
height      -1.164e-01  1.208e-02  -9.640   <2e-16 ***
body.weight  1.223e-01  1.235e-02   9.908   <2e-16 ***
avg.dbp      1.289e-01  1.119e-02  11.519   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.9378 on 7922 degrees of freedom
Multiple R-squared:  0.121, Adjusted R-squared:  0.1206 
F-statistic: 272.7 on 4 and 7922 DF,  p-value: < 2.2e-16
# Check R-squared value
summary(model_stepwise_AIC)$r.squared
[1] 0.123777
summary(model_stepwise_BIC)$r.squared
[1] 0.1210105

Both AIC and BIC stepwise model selection processes have similar R squared values. The BIC stepwise model is parsimonious. This model selects age, height, body weight, and average diastolic blood pressure as predictors for cholesterol.

Regularization-based selection techniques are useful for improving model performance and preventing overfitting, especially when you have many predictors. Two common regularization methods are Ridge regression and Lasso regression.

# Extract the response and predictors
y <- imputed_nhanes$cholestrol  # Response variable
X <- as.matrix(imputed_nhanes[, c("age", "BMI", "height", "body.weight", "avg.dbp", "avg.sbp")])

# Optional: Scale predictors (recommended for Ridge and Lasso)
X_scaled <- scale(X)

ridge_model <- glmnet(X_scaled, y, alpha = 0)  # alpha = 0 for Ridge

lasso_model <- glmnet(X_scaled, y, alpha = 1)  # alpha = 1 for Lasso

# Ridge cross-validation
cv_ridge <- cv.glmnet(X_scaled, y, alpha = 0)
plot(cv_ridge)

best_lambda_ridge <- cv_ridge$lambda.min

# Lasso cross-validation
cv_lasso <- cv.glmnet(X_scaled, y, alpha = 1)
plot(cv_lasso)

best_lambda_lasso <- cv_lasso$lambda.min


# Ridge coefficients
ridge_coefficients <- coef(cv_ridge, s = "lambda.min")
print(ridge_coefficients)
7 x 1 sparse Matrix of class "dgCMatrix"
                       s1
(Intercept)  4.291996e-15
age          2.453805e-01
BMI          4.128314e-02
height      -8.611899e-02
body.weight  7.098814e-02
avg.dbp      1.085174e-01
avg.sbp      3.889874e-02
# Lasso coefficients
lasso_coefficients <- coef(cv_lasso, s = "lambda.min")
print(lasso_coefficients)
7 x 1 sparse Matrix of class "dgCMatrix"
                       s1
(Intercept)  4.290261e-15
age          2.554719e-01
BMI          .           
height      -1.128253e-01
body.weight  1.196546e-01
avg.dbp      1.139278e-01
avg.sbp      3.029412e-02
# Predictions for Ridge
pred_ridge <- predict(cv_ridge, X_scaled, s = "lambda.min")
# Predictions for Lasso
pred_lasso <- predict(cv_lasso, X_scaled, s = "lambda.min")

# Calculate RMSE or other metrics to assess model performance

# Predictions for Ridge
pred_ridge <- predict(cv_ridge, X_scaled, s = "lambda.min")

# Predictions for Lasso
pred_lasso <- predict(cv_lasso, X_scaled, s = "lambda.min")

# Calculate RMSE for Ridge
rmse_ridge <- sqrt(mean((pred_ridge - y)^2))

# Calculate RMSE for Lasso
rmse_lasso <- sqrt(mean((pred_lasso - y)^2))

# Print RMSE values
cat("Ridge RMSE:", rmse_ridge, "\n")
Ridge RMSE: 0.9374275 
cat("Lasso RMSE:", rmse_lasso, "\n")
Lasso RMSE: 0.9372577 
# Calculate R² for Ridge
rss_ridge <- sum((pred_ridge - y)^2)  # Residual Sum of Squares
tss_ridge <- sum((y - mean(y))^2)    # Total Sum of Squares
r2_ridge <- 1 - rss_ridge / tss_ridge

# Calculate R² for Lasso
rss_lasso <- sum((pred_lasso - y)^2)
tss_lasso <- sum((y - mean(y))^2)
r2_lasso <- 1 - rss_lasso / tss_lasso

# Print R² values
cat("Ridge R²:", r2_ridge, "\n")
Ridge R²: 0.1211188 
cat("Lasso R²:", r2_lasso, "\n")
Lasso R²: 0.1214371 

The Ridge and Lasso method both had similar R squared values. Therefore, both models are recommended.

LS0tCnRpdGxlOiAnQW5hbHlzaXMgb2YgVGhlIE5hdGlvbmFsIEhlYWx0aCBhbmQgTnV0cml0aW9uIEV4YW1pbmF0aW9uIFN1cnZleSAoTkhBTkVTKScKYXV0aG9yOiAiSm9hbm5lIE11c2EiCmRhdGU6ICJGZWIgMTksIDIwMjUiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICB0b2NfZmxvYXQ6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRvY19jb2xsYXBzZWQ6IHllcwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIHNtb290aF9zY3JvbGw6IHllcwogICAgdGhlbWU6IGx1bWVuCiAgd29yZF9kb2N1bWVudDogCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBrZWVwX21kOiB5ZXMKICBwZGZfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIGZpZ193aWR0aDogMwogICAgZmlnX2hlaWdodDogMwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHs9aHRtbH0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CgovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8KCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovCiAgZm9udC1zaXplOiAyNHB4OwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLwogIGZvbnQtc2l6ZTogMjBweDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LXdlaWdodDogYm9sZDsKfQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDIycHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLwogICAgZm9udC1zaXplOiAyMHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7CiAgICBmb250LXdlaWdodDogYm9sZDsKfQoKaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7CiAgICBmb250LXdlaWdodDogYm9sZDsKfQoKaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAxNnB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGxlZnQ7CiAgICBmb250LXdlaWdodDogYm9sZDsKfQoKYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0KCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9CgpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQoKPC9zdHlsZT4KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IAojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4KCmlmICghcmVxdWlyZSgia25pdHIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpCiAgIGxpYnJhcnkoa25pdHIpCn0KCmlmICghcmVxdWlyZSgiTUFTUyIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoIk1BU1MiKQogICBsaWJyYXJ5KE1BU1MpCn0KaWYgKCFyZXF1aXJlKCJsZWFmbGV0IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygibGVhZmxldCIpCiAgIGxpYnJhcnkobGVhZmxldCkKfQoKaWYgKCFyZXF1aXJlKCJnZ3JpZGdlcyIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImdncmlkZ2VzIikKbGlicmFyeShnZ3JpZGdlcykKfQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmxpYnJhcnkodGlkeXZlcnNlKQp9CgppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKICAgbGlicmFyeShnZ3Bsb3QyKQp9CmlmICghcmVxdWlyZSgibm5ldCIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoIm5uZXQiKQogICBsaWJyYXJ5KG5uZXQpCn0KIAppZiAoIXJlcXVpcmUoIm5vcnRlc3QiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJub3J0ZXN0IikKICAgbGlicmFyeShub3J0ZXN0KQp9IAoKaWYgKCFyZXF1aXJlKCJjb3JycGxvdCIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImNvcnJwbG90IikKICAgbGlicmFyeShjb3JycGxvdCkKfQoKaWYgKCFyZXF1aXJlKCJWSU0iKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJWSU0iKQogICBsaWJyYXJ5KFZJTSkKfQoKCmlmICghcmVxdWlyZSgibWljZSIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoIm1pY2UiKQogICBsaWJyYXJ5KG1pY2UpCn0KCmlmICghcmVxdWlyZSgibmFuaWFyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygibmFuaWFyIikKICAgbGlicmFyeShuYW5pYXIpCgp9CgppZiAoIXJlcXVpcmUoIkdHYWxseSIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoIkdHYWxseSIpCiAgIGxpYnJhcnkoR0dhbGx5KQoKfQoKaWYgKCFyZXF1aXJlKCJnbG1uZXQiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJnbG1uZXQiKQogICBsaWJyYXJ5KGdsbW5ldCkKCn0KCmlmICghcmVxdWlyZSgiY2FyZXQiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpCiAgIGxpYnJhcnkoY2FyZXQpCgp9CgppZiAoIXJlcXVpcmUoInBST0MiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJwUk9DIikKICAgbGlicmFyeShwUk9DKQoKfQoKCmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICAgICAgICAgICAgICAgICAgICAgIGVjaG8gPSBUUlVFLCAgIAogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSBUUlVFLCAgICAKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQogICAgICAgICAgICAgICAgICAgICAgKSAgCmBgYAoKCgojIElOVFJPRFVDVElPTgoKCmBgYHtyfQpuaGFuZXMgPC0gcmVhZC5jc3YoImh0dHBzOi8vam11c2EzLmdpdGh1Yi5pby9qbXVzYS9uaGFuZXMuY3N2IikKCiNDUkVBVEUgTUlTU0lORyBWQUxVRVMgaW4gc29tZSB2YXJpYWJsZXMKCiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxMjMpICAKCiMgRGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgKDE1JSBvZiB0b3RhbCByb3dzKQpudW1fbWlzc2luZyA8LSByb3VuZCgwLjE0ICogbnJvdyhuaGFuZXMpKQoKIyBSYW5kb21seSBzZWxlY3Qgcm93IGluZGljZXMgdG8gaW50cm9kdWNlIG1pc3NpbmcgdmFsdWVzIAptaXNzaW5nX2luZGljZXMgPC0gc2FtcGxlKDE6bnJvdyhuaGFuZXMpLCBudW1fbWlzc2luZywgcmVwbGFjZSA9IEZBTFNFKQoKIyBTZXQgc2VsZWN0ZWQgcm93cyBpbiBib2R5LndlaWdodCB0byBOQQpuaGFuZXMkYm9keS53ZWlnaHRbbWlzc2luZ19pbmRpY2VzXSA8LSBOQQoKIyBWZXJpZnkgbWlzc2luZyB2YWx1ZXMKI3N1bShpcy5uYShuaGFuZXMkYm9keS53ZWlnaHQpKSAgCgoKIyBTZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDQ1NikgIAoKIyBEZXRlcm1pbmUgdGhlIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcyAoMTUlIG9mIHRvdGFsIHJvd3MpCm51bV9taXNzaW5nIDwtIHJvdW5kKDAuMTUgKiBucm93KG5oYW5lcykpCgojIFJhbmRvbWx5IHNlbGVjdCByb3cgaW5kaWNlcyB0byBpbnRyb2R1Y2UgbWlzc2luZyB2YWx1ZXMKbWlzc2luZ19pbmRpY2VzIDwtIHNhbXBsZSgxOm5yb3cobmhhbmVzKSwgbnVtX21pc3NpbmcsIHJlcGxhY2UgPSBGQUxTRSkKCiMgU2V0IHNlbGVjdGVkIHJvd3MgaW4gYm9keS53ZWlnaHQgdG8gTkEKbmhhbmVzJGNob2xlc3Ryb2xbbWlzc2luZ19pbmRpY2VzXSA8LSBOQQoKCiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCg3ODkpICAKCiMgRGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgKDE1JSBvZiB0b3RhbCByb3dzKQpudW1fbWlzc2luZyA8LSByb3VuZCgwLjE1ICogbnJvdyhuaGFuZXMpKQoKIyBSYW5kb21seSBzZWxlY3Qgcm93IGluZGljZXMgdG8gaW50cm9kdWNlIG1pc3NpbmcgdmFsdWVzCm1pc3NpbmdfaW5kaWNlcyA8LSBzYW1wbGUoMTpucm93KG5oYW5lcyksIG51bV9taXNzaW5nLCByZXBsYWNlID0gRkFMU0UpCgojIFNldCBzZWxlY3RlZCByb3dzIGluIHJhY2UgdG8gTkEKbmhhbmVzJHJhY2VbbWlzc2luZ19pbmRpY2VzXSA8LSBOQQoKIyBWZXJpZnkgbWlzc2luZyB2YWx1ZXMKCiNzdW0oaXMubmEobmhhbmVzJHJhY2UpKSAgCiNzdW0oaXMubmEobmhhbmVzJGNob2xlc3Ryb2wpKSAKI3N1bShpcy5uYShuaGFuZXMkYm9keS53ZWlnaHQpKSAKCiMgY3JlYXRlIGEgY29weSBvZiBuaGFuZXMgZm9yIE1JQ0UKbmhhbmVzMSA8LSBuaGFuZXMKbmhhbmVzMSRyYWNlIDwtIGFzLmZhY3RvcihuaGFuZXMxJHJhY2UpCm5oYW5lczEkY2hvbGVzdHJvbCA8LSBhcy5udW1lcmljKG5oYW5lczEkY2hvbGVzdHJvbCkKCgpgYGAKCgpUaGUgTmF0aW9uYWwgSGVhbHRoIGFuZCBOdXRyaXRpb24gRXhhbWluYXRpb24gU3VydmV5IChOSEFORVMpIGlzIGEgcHJvZ3JhbSBjb25kdWN0ZWQgYnkgdGhlIE5hdGlvbmFsIENlbnRlciBmb3IgSGVhbHRoIFN0YXRpc3RpY3MgKE5DSFMpLCBhIGRpdmlzaW9uIG9mIHRoZSBDZW50ZXJzIGZvciBEaXNlYXNlIENvbnRyb2wgYW5kIFByZXZlbnRpb24gKENEQykuIE5IQU5FUyBoYXMgYmVlbiBjb25kdWN0ZWQgaW4gdmFyaW91cyBmb3JtcyBzaW5jZSB0aGUgZWFybHkgMTk2MHMsIHdpdGggdGhlIGN1cnJlbnQgY29udGludW91cyBzdXJ2ZXkgZm9ybWF0IGJlZ2lubmluZyBpbiAxOTk5LiBUaGUgc3VydmV5IGlzIGRlc2lnbmVkIHRvIGFzc2VzcyB0aGUgaGVhbHRoIGFuZCBudXRyaXRpb25hbCBzdGF0dXMgb2YgYWR1bHRzIGFuZCBjaGlsZHJlbiBpbiB0aGUgVW5pdGVkIFN0YXRlcyB0aHJvdWdoIGEgY29tYmluYXRpb24gb2YgaW50ZXJ2aWV3cywgcGh5c2ljYWwgZXhhbWluYXRpb25zLCBhbmQgbGFib3JhdG9yeSB0ZXN0cy4gRGF0YSBpcyBjb2xsZWN0ZWQgZnJvbSBhIG5hdGlvbmFsbHkgcmVwcmVzZW50YXRpdmUgc2FtcGxlIG9mIHRoZSBVLlMuIHBvcHVsYXRpb24gdXNpbmcgYSBjb21wbGV4LCBtdWx0aXN0YWdlIHByb2JhYmlsaXR5IHNhbXBsaW5nIGRlc2lnbi4gUGFydGljaXBhbnRzIHVuZGVyZ28gaG91c2Vob2xkIGludGVydmlld3MgZm9sbG93ZWQgYnkgaGVhbHRoIGV4YW1pbmF0aW9ucyBhdCBtb2JpbGUgZXhhbWluYXRpb24gY2VudGVycyAoTUVDcyksIHdoZXJlIHRyYWluZWQgbWVkaWNhbCBwcm9mZXNzaW9uYWxzIGNvbGxlY3QgYmlvbG9naWNhbCBzYW1wbGVzIGFuZCBjb25kdWN0IGNsaW5pY2FsIGFzc2Vzc21lbnRzLiBUaGUgTkhBTkVTIGRhdGEgcHJvdmlkZXMgY3JpdGljYWwgaW5zaWdodHMgaW50byBwdWJsaWMgaGVhbHRoIHRyZW5kcywgaGVscGluZyBwb2xpY3ltYWtlcnMsIHJlc2VhcmNoZXJzLCBhbmQgaGVhbHRoY2FyZSBwcm9mZXNzaW9uYWxzIG1ha2UgaW5mb3JtZWQgZGVjaXNpb25zIG9uIG5hdGlvbmFsIGhlYWx0aCBwcmlvcml0aWVzLiBUaGUgcHVycG9zZSBvZiB0aGlzIHN0dWR5IGlzIHRvIGFzc2VzcyB0aGUgaGVhbHRoIGFuZCBudXRyaXRpb25hbCBzdGF0dXMgb2YgdGhlIHN0dWR5IHBhcnRpY2lwYW50cyB3aXRoIG1ldHJpY3Mgc3VjaCBhcyBoaWdoIGJsb29kIHByZXNzdXJlLiAKClRoZXJlIGFyZSAxNSBmZWF0dXJlcyBhbmQgNzkyNyBvYnNlcnZhdGlvbnMgaW4gdGhpcyBkYXRhIHNldC4gVGhlc2UgZmVhdHVyZXMgYXJlIGxpc3RlZCBiZWxvdy4KCjEpIG9iczogUmVzcG9uZGVudCBJZGVudGlmaWNhdGlvbiBOdW1iZXIKMikgcHN1OiBQc2V1ZG8tUFNVIDEsMiBwc3UKMykgc3RyYXR1bTogUHNldWRvLXN0cmF0dW0gKDAxIC0gNDkpCjQpIHN0YXQud2VpZ2h0OiBTdGF0aXN0aWNhbCB3ZWlnaHQgKDIyNS45MyAtIDEzOTc0NC45KQo1KSBhZ2U6IEFnZSAoeWVhcnMpIAo2KSBzZXg6IDAgPSBGZW1hbGUsIDEgPSBNYWxlIHNleAo3KSByYWNlOiBSYWNlICgxID0gV2hpdGU7IDIgPSBCbGFjazsgMyA9IE90aGVyKQo4KSBib2R5LndlaWdodDogQm9keSBXZWlnaHQgKHBvdW5kcykKOSkgaGVpZ2h0OiBTdGFuZGluZyBIZWlnaHQgKGluY2hlcykKMTApIGF2Zy5zYnA6IEF2ZXJhZ2UgU3lzdG9saWMgQlAgKG1tL0hnKSAKMTEpIGF2Zy5kYnA6IEF2ZXJhZ2UgRGlhc3RvbGljIEJQIChtbS9IZykgCjEyKSBwYXN0LnNtazogSGFzIHJlc3BvbmRlbnQgc21va2VkID4gMTAwIGNpZ2FyZXR0ZXMgaW4gbGlmZSAoMSA9IFllcywgMiA9IE5vKQoxMykgY3VycmVudC5zbWs6IERvZXMgcmVzcG9uZGVudCBzbW9rZSBjaWdhcmV0dGVzIG5vdz8gKDEgPSBZZXMsIDIgPSBObykKMTQpIGNob2xlc3Ryb2w6IFNlcnVtIENob2xlc3Rlcm9sIChtZy8xMDBtbCkgCjE1KSBoYnA6IEhpZ2ggQmxvb2QgUHJlc3N1cmUgKDAgaWYgUEVQTU5LMVIgPD0gMTQwOyAxIGlmIFBFUE1OSzFSID4gMTQwKQoKVGhlIG9icywgcHN1LCBzdHJhdHVtLCBhbmQgc3RhdC53ZWlnaHQgdmFyaWFibGVzIGFyZSBub3QgaW5jbHVkZWQgaW4gdGhpcyBhbmFseXNpcyBiZWNhdXNlIHRoZXkgZG8gbm90IGRpcmVjdGx5IHByb3ZpZGUgaGVhbHRoLXJlbGF0ZWQgaW5mb3JtYXRpb24uIAoKVGhlIGRhdGEgc2V0IGhhcyAzMTcgbWlzc2luZyB2YWx1ZXMgZm9yIGJvZHkgd2VpZ2h0IGFuZCAzOTYgbWlzc2luZyB2YWx1ZXMgZm9yIGNob2xlc3Rlcm9sLiAKCiMgRURBCgoKIyMgRGlzdHJpYnV0aW9uIG9mIEluZGl2aWR1YWwgRmVhdHVyZXMKClRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSBpbmRpdmlkdWFsIGZlYXR1cmVzIGluIHRoZSBOSEFORVMgZGF0YXNldCBwcm92aWRlIGEgc25hcHNob3Qgb2YgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgcG9wdWxhdGlvbiBpbiB0ZXJtcyBvZiB2YXJpb3VzIGhlYWx0aCBhbmQgZGVtb2dyYXBoaWMgdmFyaWFibGVzLiBVbmRlcnN0YW5kaW5nIHRoZXNlIGRpc3RyaWJ1dGlvbnMgaXMgZXNzZW50aWFsIGZvciBpZGVudGlmeWluZyBwYXR0ZXJucywgZGV0ZWN0aW5nIHBvdGVudGlhbCBiaWFzZXMsIGFuZCBhc3Nlc3NpbmcgdGhlIHJhbmdlIGFuZCB2YXJpYWJpbGl0eSBvZiBlYWNoIGZlYXR1cmUuIFRoaXMgYW5hbHlzaXMgaGVscHMgaW4gdW5kZXJzdGFuZGluZyBob3cgdmFyaWFibGVzIHN1Y2ggYXMgYWdlLCBib2R5IHdlaWdodCwgYmxvb2QgcHJlc3N1cmUsIGFuZCBsaWZlc3R5bGUgZmFjdG9ycyBhcmUgc3ByZWFkIGFjcm9zcyB0aGUgZGF0YXNldCwgd2hpY2ggaXMgY3J1Y2lhbCBmb3Igc3Vic2VxdWVudCBzdGF0aXN0aWNhbCBtb2RlbGluZyBhbmQgaW50ZXJwcmV0YXRpb24uCgpgYGB7cn0KCiMgQ29udmVydCB2YXJpYWJsZXMgdG8gZmFjdG9ycyB3aXRoIHByb3BlciBsYWJlbHMKbmhhbmVzJHNleCA8LSBmYWN0b3IobmhhbmVzJHNleCwgbGV2ZWxzID0gYygwLCAxKSwgbGFiZWxzID0gYygiRmVtYWxlIiwgIk1hbGUiKSkKbmhhbmVzJHJhY2UgPC0gZmFjdG9yKG5oYW5lcyRyYWNlLCBsZXZlbHMgPSBjKDEsIDIsIDMpLCBsYWJlbHMgPSBjKCJXaGl0ZSIsICJCbGFjayIsICJPdGhlciIpKQpuaGFuZXMkcGFzdC5zbWsgPC0gZmFjdG9yKG5oYW5lcyRwYXN0LnNtaywgbGV2ZWxzID0gYygxLCAyKSwgbGFiZWxzID0gYygiWWVzIiwgIk5vIikpCm5oYW5lcyRjdXJyZW50LnNtayA8LSBmYWN0b3IobmhhbmVzJGN1cnJlbnQuc21rLCBsZXZlbHMgPSBjKDEsIDIpLCBsYWJlbHMgPSBjKCJZZXMiLCAiTm8iKSkKCiMgQ3JlYXRlIGZyZXF1ZW5jeSB0YWJsZXMKc2V4X2NvdW50IDwtIHRhYmxlKG5oYW5lcyRzZXgpCgpyYWNlX2NvdW50IDwtIHRhYmxlKG5oYW5lcyRyYWNlKQoKcGFzdC5zbWtfY291bnQgPC0gdGFibGUobmhhbmVzJHBhc3Quc21rKQoKY3VycmVudC5zbWtfY291bnQgPC0gdGFibGUobmhhbmVzJGN1cnJlbnQuc21rKQoKCnBhcihtZnJvdyA9IGMoMiwgMikpCgojIFBsb3QgdGhlIGJhciBjaGFydHMKYmFycGxvdChoZWlnaHQgPSBzZXhfY291bnQsIGNvbCA9ICJzdGVlbGJsdWUiLCBtYWluID0gIlNleCIsIHhsYWIgPSAiU2V4IiwgeWxhYiA9ICJDb3VudCIpCgpiYXJwbG90KGhlaWdodCA9IHJhY2VfY291bnQsIGNvbCA9ICJzdGVlbGJsdWUiLCBtYWluID0gIlJhY2UiLCB4bGFiID0gIlJhY2UiLCB5bGFiID0gIkNvdW50IikKCmJhcnBsb3QoaGVpZ2h0ID0gcGFzdC5zbWtfY291bnQsIGNvbCA9ICJzdGVlbGJsdWUiLCBtYWluID0gIlBhc3QgU21va2VyIiwgeGxhYiA9ICJQYXN0IFNtb2tlciIsIHlsYWIgPSAiQ291bnQiKQoKYmFycGxvdChoZWlnaHQgPSBjdXJyZW50LnNta19jb3VudCwgY29sID0gInN0ZWVsYmx1ZSIsIG1haW4gPSAiQ3VycmVudCBTbW9rZXIiLCB4bGFiID0gIkN1cnJlbnQgU21va2VyIiwgeWxhYiA9ICJDb3VudCIpCgoKYGBgCgpUaGVyZSBhcmUgc2xpZ2h0bHkgbW9yZSBtYWxlIG9ic2VydmF0aW9ucyB0aGFuIGZlbWFsZSBvYnNlcnZhdGlvbiBpbiB0aGlzIGRhdGEgc2V0LiBUaGUgbWFqb3JpdHkgb2YgcGFydGljaXBhbnRzIGFyZSB3aGl0ZSwgZm9sbG93ZWQgYnkgYmxhY2suIE1vc3QgcGFydGljaXBhbnRzIHdlcmUgcGFzdCBzbW9rZXJzLiBJbiBmYWN0LCB0aGVyZSBpcyBhbiBpbWJhbGFuY2UgZm9yIHBhc3Qgc21va2VyLiBBYm91dCBoYWxmIG9mIGN1cnJlbnQgcGFydGljaXBhbnRzIGFyZSBjdXJyZW50IHNtb2tlcnMuIAoKYGBge3J9CgpwYXIobWZyb3cgPSBjKDIsIDIpKSAgCgojIEhpc3RvZ3JhbSBmb3IgQ2hvbGVzdGVyb2wKaGlzdChuaGFuZXMkY2hvbGVzdHJvbCwgCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgQ2hvbGVzdGVyb2wiLCAKICAgICB4bGFiID0gIkNob2xlc3Rlcm9sIiwgCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsIAogICAgIGJvcmRlciA9ICJibGFjayIsIAogICAgIGJyZWFrcyA9IDIwKSAgIyBDb250cm9sIG51bWJlciBvZiBiaW5zCiAgICAKICAgICAKIyBIaXN0b2dyYW0gZm9yIFN5c3RvbGljIEJQCmhpc3QobmhhbmVzJGF2Zy5zYnAsIAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIFN5c3RvbGljIEJQIiwgCiAgICAgeGxhYiA9ICJTeXN0b2xpYyBCUCIsIAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBib3JkZXIgPSAiYmxhY2siLCAKICAgICBicmVha3MgPSAyMCkKICAgIAoKIyBIaXN0b2dyYW0gZm9yIEJvZHkgV2VpZ2h0Cmhpc3QobmhhbmVzJGJvZHkud2VpZ2h0LCAKICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiBCb2R5IFdlaWdodCIsIAogICAgIHhsYWIgPSAiQm9keSBXZWlnaHQiLCAKICAgICBjb2wgPSAic3RlZWxibHVlIiwgCiAgICAgYm9yZGVyID0gImJsYWNrIiwgCiAgICAgYnJlYWtzID0gMjApCiAgCiAgICAgCiMgSGlzdG9ncmFtIGZvciBIZWlnaHQKaGlzdChuaGFuZXMkYm9keS53ZWlnaHQsIAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIEhlaWdodCIsIAogICAgIHhsYWIgPSAiSGVpZ2h0IiwgCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsIAogICAgIGJvcmRlciA9ICJibGFjayIsIAogICAgIGJyZWFrcyA9IDIwKSAgIAogICAgIApgYGAKCk1vc3QgcGFydGljaXBhbnRzIGRvIG5vdCBoYXZlIGhpZ2ggYmxvb2QgcHJlc3N1cmUuIFRoZSBhZ2Ugb2YgcGFydGljaXBhbnRzIHZhcmllcyBmcm9tIGFib3V0IDIwIHRvIGFib3V0IDkwLiBUaGUgaGVpZ2h0IGRpc3RyaWJ1dGlvbiBpcyByb3VnaGx5IHN5bW1ldHJpYy4gVGhlIGJvZHkgd2VpZ2h0IGRpc3RyaWJ1dGlvbiBpcyBza2V3ZWQgcmlnaHQgZHVlIHRvIHNvbWUgaGlnaCBvdXRsaWVycy4gVGhlIGhpZ2ggb3V0bGllcnMgbWF5IGluZGljYXRlIG9iZXNpdHkuIAoKCgpgYGB7cn0KCnBhcihtZnJvdyA9IGMoMiwyKSkKCmhpc3QobmhhbmVzJGF2Zy5zYnAsIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBtYWluID0gIlN5c3RvbGljIEJQIiwKICAgICB4bGFiID0gIlN5c3RvbGljIEJQIiwgeWxhYiA9ICJGcmVxdWVuY3kiLAogICAgIGJyZWFrcyA9IDMwKQoKaGlzdChuaGFuZXMkYXZnLmRicCwgY29sID0gInN0ZWVsYmx1ZSIsIAogICAgIG1haW4gPSAiRGlhc3RvbGljIEJQIiwKICAgICB4bGFiID0gIkRpYXN0b2xpYyBCUCIsIHlsYWIgPSAiRnJlcXVlbmN5IiwKICAgICBicmVha3MgPSAzMCkKCmhpc3QobmhhbmVzJGNob2xlc3Ryb2wsIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBtYWluID0gIkNob2xlc3Rlcm9sIiwKICAgICB4bGFiID0gIkNob2xlc3Rlcm9sIiwgeWxhYiA9ICJGcmVxdWVuY3kiLAogICAgIGJyZWFrcyA9IDMwKQoKCmBgYAoKU3lzdG9saWMgQlAgYW5kIGNob2xlc3Rlcm9sIGhhdmUgc2tld2VkIHJpZ2h0IGRpc3RyaWJ1dGlvbnMuIERpYXN0b2xpYyBCUCBpcyByb3VnaGx5IHN5bW1ldHJpYy4gT2YgdGhlIDc5MjcgcGFydGljaXBhbnRzLCAxOTY0IGFyZSBvYmVzZS4gCgoKIyMgUmVsYXRpb25zaGlwIGJldHdlZW4gRmVhdHVyZXMKCkV4YW1pbmluZyByZWxhdGlvbnNoaXBzIGJldHdlZW4gdmFyaWFibGVzIHVzaW5nIHNjYXR0ZXJwbG90cyBhbmQgYSBjb3JyZWxhdGlvbiBtYXRyaXggcHJvdmlkZXMgYSBjb21wcmVoZW5zaXZlIGFwcHJvYWNoIHRvIHVuZGVyc3RhbmRpbmcgYXNzb2NpYXRpb25zLiBTY2F0dGVycGxvdHMgdmlzdWFsbHkgcmV2ZWFsIGhvdyB0d28gY29udGludW91cyB2YXJpYWJsZXMgaW50ZXJhY3QsIGFsbG93aW5nIHVzIHRvIGlkZW50aWZ5IHRyZW5kcywgY29ycmVsYXRpb25zLCBhbmQgcG90ZW50aWFsIG91dGxpZXJzLiBNZWFud2hpbGUsIGEgY29ycmVsYXRpb24gbWF0cml4IHF1YW50aWZpZXMgdGhlIHN0cmVuZ3RoIGFuZCBkaXJlY3Rpb24gb2YgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIG11bHRpcGxlIHZhcmlhYmxlcyBhdCBvbmNlLCBoZWxwaW5nIHRvIHBpbnBvaW50IHdoaWNoIHBhaXJzIGFyZSBzdHJvbmdseSBjb3JyZWxhdGVkLiBUb2dldGhlciwgc2NhdHRlcnBsb3RzIGFuZCBhIGNvcnJlbGF0aW9uIG1hdHJpeCBvZmZlciB2YWx1YWJsZSBpbnNpZ2h0cyBpbnRvIHRoZSB1bmRlcmx5aW5nIHBhdHRlcm5zIGluIHRoZSBkYXRhLCBndWlkaW5nIGZ1cnRoZXIgc3RhdGlzdGljYWwgYW5hbHlzaXMgYW5kIG1vZGVsIHNlbGVjdGlvbi4KCmBgYHtyfQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMTIzKQoKIyBTYW1wbGUgNTAwIHJvd3MgKGFkanVzdCB0aGUgbnVtYmVyIGFzIG5lZWRlZCkKc2FtcGxlX2luZGljZXMgPC0gc2FtcGxlKG5yb3cobmhhbmVzKSwgc2l6ZSA9IDUwMCwgcmVwbGFjZSA9IFRSVUUpCnNhbXBsZWRfZGF0YSA8LSBuaGFuZXNbc2FtcGxlX2luZGljZXMsIGMoImFnZSIsICJib2R5LndlaWdodCIsICJoZWlnaHQiLCAiYXZnLnNicCIsICJhdmcuZGJwIiwgImNob2xlc3Ryb2wiKV0KCiMgQ3JlYXRlIGEgcGFpcndpc2Ugc2NhdHRlcnBsb3QgbWF0cml4IHdpdGggdGhlIHNhbXBsZWQgZGF0YQpwYWlycyhzYW1wbGVkX2RhdGEsIAogICAgICBtYWluID0gIlBhaXJ3aXNlIFNjYXR0ZXJwbG90cyAoQm9vdHN0cmFwcGVkIFNhbXBsZSkiLAogICAgICBwY2ggPSAxOSwgICAgICAgICAgICMgU2hhcGUgb2YgdGhlIHBvaW50cyAoY2lyY2xlKQogICAgICBjb2wgPSByZ2IoMCwgMCwgMSwgMC41KSAgIyBDb2xvciB3aXRoIHRyYW5zcGFyZW5jeSAoYmx1ZSkKKQoKYGBgCgoKCmBgYHtyfQoKbmhhbmVzX3N1YnNldCA8LSBuaGFuZXNbLCBjKCJhZ2UiLCAiYm9keS53ZWlnaHQiLCAiaGVpZ2h0IiwgImF2Zy5zYnAiLCAiYXZnLmRicCIsICJjaG9sZXN0cm9sIildCgojIENvbXB1dGUgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeApjb3JfbWF0cml4IDwtIGNvcihuaGFuZXNfc3Vic2V0LCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikKCiMgUHJpbnQgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeApwcmludChjb3JfbWF0cml4KQoKY29ycnBsb3QoY29yX21hdHJpeCwgbWV0aG9kID0gImNvbG9yIiwgdHlwZSA9ICJ1cHBlciIsIHRsLmNvbCA9ICJibGFjayIsIHRsLmNleCA9IDAuOCkKCmBgYAoKVGhlIG1vc3QgaGlnaGx5IGNvcnJlbGF0ZSBmZWF0dXJlcyBhcmUgYWdlIGFuZCBhdmVyYWdlIHN5c3RvbGljIGJsb29kIHByZXNzdXJlLCBib2R5IHdlaWdodCBhbmQgaGVpZ2h0LCBhdmVyYWdlIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIGFuZCBhdmVyYWdlIGRpYXN0b2xpYyBibG9vZCBwcmVzc3VyZSwgYWdlIGFuZCBjaG9sZXN0ZXJvbCwgYm9keSB3ZWlnaHQgYW5kIGNob2xlc3Rlcm9sLCBhbmQgYm9keSB3ZWlnaCBhbmQgYXZlcmFnZSBkaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUuIFRoZSBwYWlyd2lzZSByZWxhdGlvbnNoaXBzIGFyZSBkaXNwbGF5ZWQgd2l0aCBpbmRpdmlkdWFsIHNjYXR0ZXIgcGxvdHMuIAoKYGBge3J9CiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxMjMpCgojIEJvb3RzdHJhcCBhIHNtYWxsZXIgc2FtcGxlIChhZGp1c3Qgc2l6ZSBhcyBuZWVkZWQpCnNhbXBsZV9pbmRpY2VzIDwtIHNhbXBsZShucm93KG5oYW5lcyksIHNpemUgPSA1MDAsIHJlcGxhY2UgPSBUUlVFKQpzYW1wbGVkX2RhdGEgPC0gbmhhbmVzW3NhbXBsZV9pbmRpY2VzLCBdCgojIFNldCB1cCBhIDJ4MyBwbG90dGluZyBncmlkCnBhcihtZnJvdyA9IGMoMiwgMykpCgojIFNjYXR0ZXJwbG90IGZvciBBZ2UgYW5kIEF2Zy4gU3lzdG9saWMgQmxvb2QgUHJlc3N1cmUKcGxvdChzYW1wbGVkX2RhdGEkYWdlLCBzYW1wbGVkX2RhdGEkYXZnLnNicCwgbWFpbiA9ICJBZ2UgdnMgQXZnLiBTeXN0b2xpYyBCUCIsIAogICAgIHhsYWIgPSAiQWdlIiwgeWxhYiA9ICJBdmcuIFN5c3RvbGljIEJQIiwgY29sID0gImJsdWUiLCBwY2ggPSAxOSkKCiMgU2NhdHRlcnBsb3QgZm9yIEJvZHkgV2VpZ2h0IGFuZCBIZWlnaHQKcGxvdChzYW1wbGVkX2RhdGEkYm9keS53ZWlnaHQsIHNhbXBsZWRfZGF0YSRoZWlnaHQsIG1haW4gPSAiQm9keSBXZWlnaHQgdnMgSGVpZ2h0IiwgCiAgICAgeGxhYiA9ICJCb2R5IFdlaWdodCIsIHlsYWIgPSAiSGVpZ2h0IiwgY29sID0gInJlZCIsIHBjaCA9IDE5KQoKIyBTY2F0dGVycGxvdCBmb3IgQXZnLiBTeXN0b2xpYyBCbG9vZCBQcmVzc3VyZSBhbmQgQXZnLiBEaWFzdG9saWMgQmxvb2QgUHJlc3N1cmUKcGxvdChzYW1wbGVkX2RhdGEkYXZnLnNicCwgc2FtcGxlZF9kYXRhJGF2Zy5kYnAsIG1haW4gPSAiQXZnLiBTeXN0b2xpYyB2cyBBdmcuIERpYXN0b2xpYyBCUCIsIAogICAgIHhsYWIgPSAiQXZnLiBTeXN0b2xpYyBCUCIsIHlsYWIgPSAiQXZnLiBEaWFzdG9saWMgQlAiLCBjb2wgPSAiZm9yZXN0Z3JlZW4iLCBwY2ggPSAxOSkKCiMgU2NhdHRlcnBsb3QgZm9yIEFnZSBhbmQgQ2hvbGVzdGVyb2wKcGxvdChzYW1wbGVkX2RhdGEkYWdlLCBzYW1wbGVkX2RhdGEkY2hvbGVzdHJvbCwgbWFpbiA9ICJBZ2UgdnMgQ2hvbGVzdGVyb2wiLCAKICAgICB4bGFiID0gIkFnZSIsIHlsYWIgPSAiQ2hvbGVzdGVyb2wiLCBjb2wgPSAicHVycGxlIiwgcGNoID0gMTkpCgojIFNjYXR0ZXJwbG90IGZvciBBdmcuIFN5c3RvbGljIEJQIGFuZCBDaG9sZXN0ZXJvbApwbG90KHNhbXBsZWRfZGF0YSRhdmcuc2JwLCBzYW1wbGVkX2RhdGEkY2hvbGVzdHJvbCwgbWFpbiA9ICJBdmcgU3lzdG9saWMgQlAgdnMgQ2hvbGVzdGVyb2wiLCAKICAgICB4bGFiID0gIkNob2xlc3Rlcm9sIiwgeWxhYiA9ICJBdmcuU0JQIiwgY29sID0gImdvbGQiLCBwY2ggPSAxOSkKCiMgU2NhdHRlcnBsb3QgZm9yIEJvZHkgV2VpZ2h0IGFuZCBBdmcuIERpYXN0b2xpYyBCbG9vZCBQcmVzc3VyZQpwbG90KHNhbXBsZWRfZGF0YSRib2R5LndlaWdodCwgc2FtcGxlZF9kYXRhJGF2Zy5kYnAsIG1haW4gPSAiQm9keSBXZWlnaHQgdnMgQXZnLiBEaWFzdG9saWMgQlAiLCAKICAgICB4bGFiID0gIkJvZHkgV2VpZ2h0IiwgeWxhYiA9ICJBdmcuIERpYXN0b2xpYyBCUCIsIGNvbCA9ICJtYXJvb24iLCBwY2ggPSAxOSkKYGBgCgpTaXggb2YgdGhlIG1vc3QgY29ycmVsYXRlZCBwYWlyd2lzZSByZWxhdGlvbnNoaXAgYXJlIHZpc3VhbGl6ZWQgaW4gc2NhdHRlciBwbG90cy4gQWxsIHBhaXJ3aXNlIHJlbGF0aW9uc2hpcHMgYXJlIGxpbmVhciB3aXRoIG5vIGFwcGFyZW50IGNsdXN0ZXJpbmcuIAoKCmBgYHtyfQoKIyBTZXQgdXAgYSAyeDIgcGxvdHRpbmcgZ3JpZApwYXIobWZyb3cgPSBjKDIsIDIpKQoKIyBDcmVhdGUgYSBjb250aW5nZW5jeSB0YWJsZQpzZXhfaGJwX3RhYmxlIDwtIHRhYmxlKG5oYW5lcyRzZXgsIG5oYW5lcyRoYnApCgojIEdlbmVyYXRlIG1vc2FpYyBwbG90Cm1vc2FpY3Bsb3Qoc2V4X2hicF90YWJsZSwgCiAgICAgICAgICAgY29sID0gYygiZ29sZCIsICJyb3lhbGJsdWUiKSwgCiAgICAgICAgICAgbWFpbiA9ICJTZXggYW5kIEhpZ2ggQmxvb2QgUHJlc3N1cmUiLAogICAgICAgICAgIHhsYWIgPSAiU2V4IFN0YXR1cyIsIAogICAgICAgICAgIHlsYWIgPSAiSEJQIFN0YXR1cyIsCiAgICAgICAgICAgYm9yZGVyID0gImJsYWNrIikKCgojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlCnJhY2VfaGJwX3RhYmxlIDwtIHRhYmxlKG5oYW5lcyRyYWNlLCBuaGFuZXMkaGJwKQoKIyBHZW5lcmF0ZSBtb3NhaWMgcGxvdAptb3NhaWNwbG90KHJhY2VfaGJwX3RhYmxlLCAKICAgICAgICAgICBjb2wgPSBjKCJnb2xkIiwgInJveWFsYmx1ZSIpLCAKICAgICAgICAgICBtYWluID0gIlJhY2UgYW5kIEhpZ2ggQmxvb2QgUHJlc3N1cmUiLAogICAgICAgICAgIHhsYWIgPSAiUmFjZSIsIAogICAgICAgICAgIHlsYWIgPSAiSEJQIFN0YXR1cyIsCiAgICAgICAgICAgYm9yZGVyID0gImJsYWNrIikKCgojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlCmN1cnJlbnQuc21rX2hicF90YWJsZSA8LSB0YWJsZShuaGFuZXMkY3VycmVudC5zbWssIG5oYW5lcyRoYnApCgojIEdlbmVyYXRlIG1vc2FpYyBwbG90Cm1vc2FpY3Bsb3QoY3VycmVudC5zbWtfaGJwX3RhYmxlLCAKICAgICAgICAgICBjb2wgPSBjKCJnb2xkIiwgInJveWFsYmx1ZSIpLCAKICAgICAgICAgICBtYWluID0gIkN1cnJlbnQgU21va2luZyBTdGF0dXMgYW5kIEhCUCIsCiAgICAgICAgICAgeGxhYiA9ICJDdXJyZW50IFNtb2tpbmcgU3RhdHVzIiwgCiAgICAgICAgICAgeWxhYiA9ICJIQlAgU3RhdHVzIiwKICAgICAgICAgICBib3JkZXIgPSAiYmxhY2siKQoKCiMgQ3JlYXRlIGEgY29udGluZ2VuY3kgdGFibGUKcGFzdC5zbWtfaGJwX3RhYmxlIDwtIHRhYmxlKG5oYW5lcyRwYXN0LnNtaywgbmhhbmVzJGhicCkKCiMgR2VuZXJhdGUgbW9zYWljIHBsb3QKbW9zYWljcGxvdChwYXN0LnNta19oYnBfdGFibGUsIAogICAgICAgICAgIGNvbCA9IGMoImdvbGQiLCAicm95YWxibHVlIiksIAogICAgICAgICAgIG1haW4gPSAiUGFzdCBTbW9raW5nIFN0YXR1cyBhbmQgSEJQIiwKICAgICAgICAgICB4bGFiID0gIlBhc3QgU21va2luZyBTdGF0dXMiLCAKICAgICAgICAgICB5bGFiID0gIkhCUCBTdGF0dXMiLAogICAgICAgICAgIGJvcmRlciA9ICJibGFjayIpCmBgYAoKUmVsYXRpb25zaGlwIGJldHdlZW4gaGlnaCBibG9vZCBwcmVzc3VyZSBhbmQgcmFjZSwgc2V4LCBhbmQgY3VycmVudCBzbW9raW5nIHN0YXR1cyBjYW4gYmUgdmlzdWFsaXplZCBpbiB0aGUgbW9zYWljIHBsb3RzLiBUaGVyZSBhcHBlYXJzIHRvIGJlIGFuIGFzc29jaWF0aW9uIGJldHdlZW4gaGlnaCBjdXJyZW50IHNtb2tpbmcgYW5kIHJhY2Ugd2l0aCBoaWdoIGJsb29kIHByZXNzdXJlLiBBIGxlc3MgcHJvbm91bmNlZCByZWxhdGlvbnNoaXAgY2FuIGJlZW4gc2VlbiBiZXR3ZWVuIHNleCBhbmQgcmFjZSB3aXRoIGhpZ2ggYmxvb2QgcHJlc3N1cmUuIAoKCmBgYHtyfQoKIyBTZXQgdXAgYSAyeDIgcGxvdHRpbmcgZ3JpZApwYXIobWZyb3cgPSBjKDEsIDIpKQoKCiMgQ3JlYXRlIGEgY29udGluZ2VuY3kgdGFibGUKY3VycmVudC5zbWtfcmFjZV90YWJsZSA8LSB0YWJsZShuaGFuZXMkcmFjZSwgbmhhbmVzJGN1cnJlbnQuc21rKQoKIyBHZW5lcmF0ZSBtb3NhaWMgcGxvdAptb3NhaWNwbG90KGN1cnJlbnQuc21rX3JhY2VfdGFibGUsIAogICAgICAgICAgIGNvbCA9IGMoImdvbGQiLCAicm95YWxibHVlIiksIAogICAgICAgICAgIG1haW4gPSAiUmFjZSBhbmQgQ3VycmVudCBTbW9rZSBTdGF0dXMiLAogICAgICAgICAgIHhsYWIgPSAiUmFjZSIsIAogICAgICAgICAgIHlsYWIgPSAiQ3VycmVudCBTbW9rZSIsCiAgICAgICAgICAgYm9yZGVyID0gImJsYWNrIikKCgojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlCmN1cnJlbnQuc21rX3NleF90YWJsZSA8LSB0YWJsZShuaGFuZXMkc2V4LCBuaGFuZXMkY3VycmVudC5zbWspCgojIEdlbmVyYXRlIG1vc2FpYyBwbG90Cm1vc2FpY3Bsb3QoY3VycmVudC5zbWtfc2V4X3RhYmxlLCAKICAgICAgICAgICBjb2wgPSBjKCJnb2xkIiwgInJveWFsYmx1ZSIpLCAKICAgICAgICAgICBtYWluID0gIlNleCBhbmQgQ3VycmVudCBTbW9rZSBTdGF0dXMiLAogICAgICAgICAgIHhsYWIgPSAiU2V4IiwgCiAgICAgICAgICAgeWxhYiA9ICJDdXJyZW50IFNtb2tlIiwKICAgICAgICAgICBib3JkZXIgPSAiYmxhY2siKQoKIyBDcmVhdGUgYSBjb250aW5nZW5jeSB0YWJsZQpwYXN0LnNta19yYWNlX3RhYmxlIDwtIHRhYmxlKG5oYW5lcyRyYWNlLCBuaGFuZXMkcGFzdC5zbWspCgoKCmBgYAoKQW4gYXNzb2NpYXRpb24gZXhpc3RzIGJldHdlZW4gcmFjZSBhbmQgY3VycmVudCBzbW9raW5nLiBCbGFjayBpbmRpdmlkdWFscyB0ZW5kIHRvIHNtb2tlIG1vcmUgdGhhbiB3aGl0ZSBvciBvdGhlciBpbmRpdmlkdWFscy4gQW4gYXNzb2NpYXRpb24gZXhpc3RzIGJldHdlZW4gc2V4IGFuZCBjdXJyZW50IHNtb2tlIHN0YXR1cy4gRmVtYWxlIHRlbmQgdG8gc21va2UgY3VycmVudGx5IHNsaWdodCBtb3JlIHRoYW4gbWFsZXMgaW4gdGhpcyBwb3B1bGF0aW9uLiAKCgoKIyBNSVNTSU5HIFZBTFVFUwoKIyMgSW1wdXRhdGlvbiBmb3IgQ2F0ZWdvcmljYWwgRmVhdHVyZXMKClRoZSBkaXN0cmlidXRpb24gb2YgbWlzc2luZyB2YWx1ZXMgZm9yIGlzIGV4YW1pbmVkIHRvIGRldGVybWluZSBhbnkgcGF0dGVybnMuIFRoaXMgc3RlcCBpcyBkb25lIHRvIGVuc3VyZSB0aGF0IHZhbHVlIGFyZSBtaXNzaW5nIGF0IHJhbmRvbS4gCgpgYGB7cn0KIyBTZXQgdXAgYSAyeDIgcGxvdHRpbmcgZ3JpZApwYXIobWZyb3cgPSBjKDIsIDIpKQoKI3N1bShpcy5uYShuaGFuZXMkcmFjZSkpCgojbWVhbihpcy5uYShuaGFuZXMkcmFjZSkpCgojdGFibGUobmhhbmVzJHJhY2UsIHVzZU5BID0gImlmYW55IikKCgojZ2dwbG90KG5oYW5lcywgYWVzKHggPSBmYWN0b3IocmFjZSkpKSArCiAgI2dlb21fYmFyKGZpbGwgPSAiYmx1ZSIpICsKICAjbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgUmFjZSAoQmVmb3JlIEltcHV0YXRpb24pIiwgeCA9ICJSYWNlIiwgeSA9ICJDb3VudCIpICsKICAjdGhlbWVfbWluaW1hbCgpCgoKYWdncihuaGFuZXMsIGNvbCA9IGMoIm5hdnlibHVlIiwgInJlZCIpLCBudW1iZXJzID0gVFJVRSwgc29ydFZhcnMgPSBUUlVFLCAKICAgICBsYWJlbHMgPSBuYW1lcyhuaGFuZXMpLCBjZXguYXhpcyA9IDAuNywgZ2FwID0gMiwgCiAgICAgeWxhYiA9IGMoIk1pc3NpbmcgRGF0YSBPdmVydmlldyIsICJQYXR0ZXJuIG9mIE1pc3NpbmcgRGF0YSIpKQoKYGBgCgpBIHBhdHRlcm4gb2YgbWlzc2luZyB2YWx1ZXMgZXhpc3RzIGJldHdlZW4gYm9keSB3ZWlnaHQsIEJNSSwgYW5kIG9iZXNlLiBUaGlzIHBhdHRlcm4gY2FuIGJlIGV4cGxhaW5lZCBieSB0aGUgZmFjdCB0aGF0IGJvZHkgd2VpZ2h0IGlzIHVzZWQgdG8gY2FsY3VsYXRlIEJNSSwgd2hpY2ggaXMgdGhlbiB1c2VkIHRvIGNsYXNzaWZ5IG9ic2VydmF0aW9ucyBmb3Igb2Jlc2UuIEhvd2V2ZXIsIHRoZSBwYXR0ZXJuIG9mIG1pc3NpbmcgdmFsdWUgYXBwZWFycyB0byBiZSByYW5kb20gZm9yIHRoZSBvdGhlciBmZWF0dXJlcy4gCgpObyBwYXR0ZXJuIG9mIG1pc3NpbmcgdmFsdWVzIGV4aXN0cyBiZXR3ZWVuIHJhY2UgYW5kIG90aGVyIGZlYXR1cmVzLCBJbiB0dXJuLCBrTk4gaW1wdXRhdGlvbiBpcyB1c2VkIGZvciBtaXNzaW5nIHZhbHVlcyBvZiByYWNlLiAgCgoKCmBgYHtyfQoKc3VtKGlzLm5hKG5oYW5lcyRyYWNlKSkKdGFibGUobmhhbmVzJHJhY2UsIHVzZU5BID0gImlmYW55IikgICMgQ2hlY2sgZGlzdHJpYnV0aW9uIGluY2x1ZGluZyBOQXMKCgojIFBlcmZvcm0gS05OIEltcHV0YXRpb24gKHNpbmdsZSBpbXB1dGF0aW9uKQpuaGFuZXNfaW1wdXRlZCA8LSBrTk4obmhhbmVzLCB2YXJpYWJsZSA9ICJyYWNlIiwgayA9IDMpCgojIEtlZXAgb25seSB0aGUgb3JpZ2luYWwgY29sdW1ucyAod2l0aG91dCBleHRyYSBjb2x1bW5zIGFkZGVkIGJ5IGtOTikKbmhhbmVzX2ltcHV0ZWQgPC0gbmhhbmVzX2ltcHV0ZWRbLCBuYW1lcyhuaGFuZXMpXQoKCgojIENPTVBBUkUgYmVmb3JlIGFuZCBhZnRlciBpbXB1dGF0aW9uIGRpc3RyaWJ1dGlvbgoKcGFyKG1mcm93ID0gYygxLDIpKSAgIyBTcGxpdCBwbG90dGluZyBhcmVhIGludG8gdHdvCgojIENvbnZlcnQgcmFjZSB0byBhIGZhY3RvciBmb3IgYmV0dGVyIHZpc3VhbGl6YXRpb24KbmhhbmVzJHJhY2UgPC0gYXMuZmFjdG9yKG5oYW5lcyRyYWNlKQpuaGFuZXNfaW1wdXRlZCRyYWNlIDwtIGFzLmZhY3RvcihuaGFuZXNfaW1wdXRlZCRyYWNlKQoKIyBDcmVhdGUgYSBuZXcgY29sdW1uIHRvIGluZGljYXRlIHdoZXRoZXIgdGhlIGRhdGEgaXMgb3JpZ2luYWwgb3IgaW1wdXRlZApuaGFuZXMkc291cmNlIDwtICJCZWZvcmUgSW1wdXRhdGlvbiIKbmhhbmVzX2ltcHV0ZWQkc291cmNlIDwtICJBZnRlciBJbXB1dGF0aW9uIgoKIyBDb21iaW5lIGJvdGggZGF0YXNldHMKbmhhbmVzX2NvbXBhcmUgPC0gYmluZF9yb3dzKG5oYW5lcywgbmhhbmVzX2ltcHV0ZWQpCgojIFBsb3QgdGhlIGRpc3RyaWJ1dGlvbiBiZWZvcmUgYW5kIGFmdGVyIGltcHV0YXRpb24KZ2dwbG90KG5oYW5lc19jb21wYXJlLCBhZXMoeCA9IHJhY2UsIGZpbGwgPSBzb3VyY2UpKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgUmFjZSBCZWZvcmUgYW5kIEFmdGVyIEltcHV0YXRpb24iLAogICAgICAgeCA9ICJSYWNlIiwKICAgICAgIHkgPSAiQ291bnQiLAogICAgICAgZmlsbCA9ICJEYXRhc2V0IikgKwogIHRoZW1lX21pbmltYWwoKQoKaW1wdXRlZF9uaGFuZXMgPC0gbmhhbmVzX2ltcHV0ZWQKCgpgYGAKVGhlIG92ZXJhbGwgZGlzdHJpYnV0aW9uIG9mIHJhY2UgYWZ0ZXIgaW1wdXRhdGlvbiBjaGFuZ2VkIHNsaWdodGx5IGFmdGVyIGltcHV0YXRpb24uCgoKIyMgUmVncmVzc2lvbi1CYXNlZCBJbXB1dGF0aW9uIGZvciBOdW1lcmljYWwgRmVhdHVyZQoKVGhlIHBhdHRlcm4gb2YgbWlzc2luZyB2YWx1ZXMgZm9yIGJvZHkgd2VpZ2h0IGFuZCBjaG9sZXN0ZXJvbCBpcyBleGFtaW5lZCBmb3IgYW55IHBvdGVudGlhbCBwYXR0ZXJucy4KCmBgYHtyfQoKCiN2aXNfbWlzcyhuaGFuZXNbLCBjKCJib2R5LndlaWdodCIsICJjaG9sZXN0cm9sIildKQoKCiMgU2V0IHNtYWxsZXIgZm9udCBzaXplIGZvciB0aGUgcGxvdApwYXIoY2V4ID0gMC40KSAgIyBBZGp1c3QgdGhpcyB2YWx1ZSBmb3IgZGVzaXJlZCBmb250IHNpemUKCiMgR2VuZXJhdGUgdGhlIG1pc3NpbmcgZGF0YSBwYXR0ZXJuIHBsb3QKCm1kLnBhdHRlcm4oaW1wdXRlZF9uaGFuZXMpCgojIFJlc2V0IGdyYXBoaWNzIHNldHRpbmdzIChvcHRpb25hbCkKcGFyKGNleCA9IDEpCgoKYGBgCkEgcGF0dGVybiBvZiBtaXNzaW5nIHZhbHVlcyBleGlzdHMgYmV0d2VlbiBib2R5IHdlaWdodCwgQk1JLCBhbmQgb2Jlc2UgYmVjYXVzZQoKCkJvZHkgd2VpZ2h0IGlzIGNvcnJlbGF0ZWQgd2l0aCBoZWlnaHQsIGF2ZXJhZ2UgZGlhc3RvbGljIGJsb29kIHByZXNzdXJlLCBhbmQgc2V4LiBJbiB0dXJuLCB0aGUgbWlzc2luZyBib2R5IHdlaWdodCB2YWx1ZXMgd2lsbCBiZSBpbXB1dGVkIHVzaW5nIGEgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBzYWlkIGV4cGxhbmF0b3J5IGZlYXR1cmVzLiAKCmBgYHtyfQoKIyBTdWJzZXQgbmhhbmVzIHRvIGNvbnRhaW4gb25seSBjb21wbGV0ZSBvYnNlcnZhdGlvbnMgZm9yIHNlbGVjdGVkIHZhcmlhYmxlcwpjb21wbGV0ZV9uaGFuZXMgPC0gaW1wdXRlZF9uaGFuZXNbY29tcGxldGUuY2FzZXMoaW1wdXRlZF9uaGFuZXNbLCBjKCJib2R5LndlaWdodCIsICJoZWlnaHQiLCAiYXZnLmRicCIsICJzZXgiKV0pLCBdCgojIEZpdCB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwKbW9kZWxfYm9keS53ZWlnaHQgPC0gbG0oYm9keS53ZWlnaHQgfiBoZWlnaHQgKyBhdmcuZGJwICsgc2V4LCBkYXRhID0gY29tcGxldGVfbmhhbmVzKQoKIyBWaWV3IHRoZSBtb2RlbCBzdW1tYXJ5CnN1bW1hcnkobW9kZWxfYm9keS53ZWlnaHQpCgojIFBsb3QgdGhlIHJlc2lkdWFscwpwbG90KG1vZGVsX2JvZHkud2VpZ2h0JHJlc2lkdWFscykKCgpgYGAKCgpUaGUgcmVzaWR1YWwgcGxvdCBzaG93cyBubyBwYXR0ZXJucy4gSW4gdHVybiwgdGhlIG1vZGVsIGZpdCB0aGUgZGF0YSB3ZWxsIGFuZCB0aGUgYXNzdW1wdGlvbiBvZiBjb25zdGFudCB2YXJpYW5jZSBvZiByZXNpZHVhbHMgaXMgc2F0aXNmaWVkLgoKYGBge3J9CgojIENyZWF0ZSBhIGNvcHkgb2YgdGhlIGRhdGFzZXQgdG8gd29yayB3aXRoCiNpbXB1dGVkX25oYW5lcyA8LSBjb21wbGV0ZV9uaGFuZXMKCmNvbFN1bXMoaXMubmEoaW1wdXRlZF9uaGFuZXMpKQojIElkZW50aWZ5IHRoZSByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMgZm9yIGJvZHkud2VpZ2h0Cm1pc3NpbmdfYm9keV93ZWlnaHQgPC0gaXMubmEoaW1wdXRlZF9uaGFuZXMkYm9keS53ZWlnaHQpCgojIFVzZSB0aGUgbW9kZWwgdG8gcHJlZGljdCB0aGUgbWlzc2luZyB2YWx1ZXMKaW1wdXRlZF9uaGFuZXMkYm9keS53ZWlnaHRbbWlzc2luZ19ib2R5X3dlaWdodF0gPC0gcHJlZGljdChtb2RlbF9ib2R5LndlaWdodCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGltcHV0ZWRfbmhhbmVzW21pc3NpbmdfYm9keV93ZWlnaHQsIGMoImhlaWdodCIsICJhdmcuZGJwIiwgInNleCIsICJhZ2UiKV0pCgojIENoZWNrIHRoZSBkYXRhc2V0CiNoZWFkKGltcHV0ZWRfbmhhbmVzKQoKIyBDcmVhdGUgYSBwbG90IHRvIG92ZXJsYXkgdGhlIGRlbnNpdHkgY3VydmVzCmdncGxvdCgpICsKICBnZW9tX2RlbnNpdHkoZGF0YSA9IGNvbXBsZXRlX25oYW5lcywgYWVzKHggPSBib2R5LndlaWdodCksIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC4yLCBuYS5ybSA9IFRSVUUpICsgCiAgZ2VvbV9kZW5zaXR5KGRhdGEgPSBpbXB1dGVkX25oYW5lcywgYWVzKHggPSBib2R5LndlaWdodCksIGZpbGwgPSAicmVkIiwgYWxwaGEgPSAwLjIsIG5hLnJtID0gVFJVRSkgKwogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIEJvZHkgV2VpZ2h0OiBCZWZvcmUgYW5kIEFmdGVyIEltcHV0YXRpb24iLAogICAgICAgeCA9ICJCb2R5IFdlaWdodCIsIHkgPSAiRGVuc2l0eSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiQm9keSBXZWlnaHQiLCB2YWx1ZXMgPSBjKCJibHVlIiA9ICJibHVlIiwgInJlZCIgPSAicmVkIiksIAogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkJlZm9yZSBJbXB1dGF0aW9uIiwgIkFmdGVyIEltcHV0YXRpb24iKSkKCgpgYGAKCgpUaGUgZGlzdHJpYnV0aW9uIGJlZm9yZSBhbmQgYWZ0ZXIgaW1wdXRhdGlvbiBpcyBzaW1pbGFyLiBJbiB0dXJuLCB0aGUgaW1wdXRhdGlvbiBkaWQgbm90IGNoYW5nZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGJvZHkgd2VpZ2h0LiAKCgoKClRoZSBmZWF0dXJlcyB0aGF0IGFyZSBtb3N0IGNvcnJlbGF0ZWQgdG8gY2hvbGVzdGVyb2wgYXJlIGFnZSwgYXZlcmFnZSBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSwgYW5kIGJvZHkgd2VpZ2h0LiBJbiB0dXJuLCB0aGUgbWlzc2luZyB2YWx1ZXMgb2YgY2hvbGVzdGVyb2wgd2lsbCBiZSBpbXB1dGVkIHdpdGggYSBtb2RlbCB1c2luZyB0aGVzZSBmZWF0dXJlcy4gCgpgYGB7cn0KCgoKIyBTdWJzZXQgbmhhbmVzIHRvIGluY2x1ZGUgb25seSByb3dzIHdpdGggY29tcGxldGUgY2FzZXMgZm9yIGNob2xlc3Ryb2wsIGFnZSwgYXZnLnNicCwgYW5kIEJNSQpjb21wbGV0ZV9uaGFuZXMgPC0gaW1wdXRlZF9uaGFuZXNbIWlzLm5hKGltcHV0ZWRfbmhhbmVzJGNob2xlc3Ryb2wpICYgIWlzLm5hKGltcHV0ZWRfbmhhbmVzJGFnZSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKGltcHV0ZWRfbmhhbmVzJGF2Zy5zYnApICYgIWlzLm5hKGltcHV0ZWRfbmhhbmVzJGJvZHkud2VpZ2h0KSwgXQoKY29tcGxldGVfbmhhbmVzIDwtIGltcHV0ZWRfbmhhbmVzW2NvbXBsZXRlLmNhc2VzKGltcHV0ZWRfbmhhbmVzWywgYygiY2hvbGVzdHJvbCIsICJhZ2UiLCAiYXZnLnNicCIsICJib2R5LndlaWdodCIpXSksIF0KCiMgRml0IHRoZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB0byBwcmVkaWN0IGNob2xlc3Ryb2wKbW9kZWxfY2hvbGVzdHJvbCA8LSBsbShjaG9sZXN0cm9sIH4gYWdlICsgYXZnLnNicCArIGJvZHkud2VpZ2h0LCBkYXRhID0gY29tcGxldGVfbmhhbmVzKQpzdW1tYXJ5KG1vZGVsX2Nob2xlc3Ryb2wpCgoKIyBWaWV3IHRoZSBtb2RlbCBzdW1tYXJ5CnN1bW1hcnkobW9kZWxfY2hvbGVzdHJvbCkKCiMgQ3JlYXRlIGEgY29weSBvZiBuaGFuZXMgZm9yIGltcHV0YXRpb24KI2ltcHV0ZWRfbmhhbmVzIDwtIG5oYW5lcwoKIyBJZGVudGlmeSByb3dzIHdpdGggbWlzc2luZyBjaG9sZXN0cm9sIHZhbHVlcwptaXNzaW5nX2Nob2xlc3Ryb2wgPC0gaXMubmEoaW1wdXRlZF9uaGFuZXMkY2hvbGVzdHJvbCkKCiMgVXNlIHRoZSBtb2RlbCB0byBwcmVkaWN0IHRoZSBtaXNzaW5nIGNob2xlc3Ryb2wgdmFsdWVzCmltcHV0ZWRfbmhhbmVzJGNob2xlc3Ryb2xbbWlzc2luZ19jaG9sZXN0cm9sXSA8LSBwcmVkaWN0KG1vZGVsX2Nob2xlc3Ryb2wsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGltcHV0ZWRfbmhhbmVzW21pc3NpbmdfY2hvbGVzdHJvbCwgYygiYWdlIiwgImF2Zy5zYnAiLCAiYm9keS53ZWlnaHQiKV0pCgoKCgpgYGAKCkFsbCBleHBsYW5hdG9yeSBmZWF0dXJlcyBhcmUgc2lnbmlmaWNhbnQuIAoKYGBge3J9CiMgR2V0IHJlc2lkdWFscyBmcm9tIHRoZSBtb2RlbCBmb3IgY29tcGxldGVfbmhhbmVzCnJlc2lkdWFsc19jaG9sZXN0cm9sIDwtIHJlc2lkdWFscyhtb2RlbF9jaG9sZXN0cm9sKQoKIyBQcmVkaWN0ZWQgdmFsdWVzIGZvciBjb21wbGV0ZV9uaGFuZXMKcHJlZGljdGVkX2Nob2xlc3Ryb2wgPC0gcHJlZGljdChtb2RlbF9jaG9sZXN0cm9sLCBuZXdkYXRhID0gY29tcGxldGVfbmhhbmVzKQoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciByZXNpZHVhbHMgYW5kIHByZWRpY3RlZCB2YWx1ZXMKcmVzaWR1YWxzX2RhdGEgPC0gZGF0YS5mcmFtZSgKICBwcmVkaWN0ZWRfY2hvbGVzdHJvbCA9IHByZWRpY3RlZF9jaG9sZXN0cm9sLAogIHJlc2lkdWFsc19jaG9sZXN0cm9sID0gcmVzaWR1YWxzX2Nob2xlc3Ryb2wKKQoKIyBDcmVhdGUgdGhlIHJlc2lkdWFsIHBsb3QKbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QocmVzaWR1YWxzX2RhdGEsIGFlcyh4ID0gcHJlZGljdGVkX2Nob2xlc3Ryb2wsIHkgPSByZXNpZHVhbHNfY2hvbGVzdHJvbCkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiKSArICAjIFBsb3QgdGhlIHJlc2lkdWFscwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsgICMgQWRkIGEgaG9yaXpvbnRhbCBsaW5lIGF0IDAKICBsYWJzKHRpdGxlID0gIlJlc2lkdWFsIFBsb3QgZm9yIENob2xlc3Ryb2wgUHJlZGljdGlvbiIsCiAgICAgICB4ID0gIlByZWRpY3RlZCBDaG9sZXN0cm9sIiwKICAgICAgIHkgPSAiUmVzaWR1YWxzIikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBDcmVhdGUgdGhlIG92ZXJsYXkgZGVuc2l0eSBwbG90CmdncGxvdCgpICsKICBnZW9tX2RlbnNpdHkoZGF0YSA9IG5oYW5lcywgYWVzKHggPSBjaG9sZXN0cm9sKSwgZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjIsIG5hLnJtID0gVFJVRSkgKyAgIyBCZWZvcmUgaW1wdXRhdGlvbgogIGdlb21fZGVuc2l0eShkYXRhID0gaW1wdXRlZF9uaGFuZXMsIGFlcyh4ID0gY2hvbGVzdHJvbCksIGZpbGwgPSAicmVkIiwgYWxwaGEgPSAwLjIsIG5hLnJtID0gVFJVRSkgKyAgIyBBZnRlciBpbXB1dGF0aW9uCiAgbGFicyh0aXRsZSA9ICJEZW5zaXR5IFBsb3Qgb2YgQ2hvbGVzdHJvbDogQmVmb3JlIGFuZCBBZnRlciBJbXB1dGF0aW9uIiwKICAgICAgIHggPSAiQ2hvbGVzdHJvbCIsIHkgPSAiRGVuc2l0eSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiQ2hvbGVzdHJvbCIsIHZhbHVlcyA9IGMoImJsdWUiID0gImJsdWUiLCAicmVkIiA9ICJyZWQiKSwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQmVmb3JlIEltcHV0YXRpb24iLCAiQWZ0ZXIgSW1wdXRhdGlvbiIpKQoKCgpgYGAKClRoZSBkaXN0cmlidXRpb24gb2YgY2hvbGVzdGVyb2wgZGlkIG5vdCBjaGFuZ2Ugc2lnbmlmaWNhbnRseSBhZnRlciBpbXB1dGF0aW9uLiBBcyB3ZWxsLCB0aGUgcmVzaWR1YWwgcGxvdCBkb2VzIG5vdCBkaXNwbGF5IGFueSBwYXR0ZXJucy4gSW4gdHVybiwgdGhlIG1vZGVsIGZpdCB0aGUgZGF0YSB3ZWxsIGFuZCB0aGUgYXNzdW1wdGlvbiBvZiBjb25zdGFudCB2YXJpYW5jZSBvZiByZXNpZHVhbHMgaXMgc2F0aXNmaWVkLiAKCiMjIEZlYXR1cmUgRW5naW5lZXIKCkluIHR1cm4gYSBmZWF0dXJlIHdoaWNoIGNhbGN1bGF0ZXMgQk1JIGlzIGNyZWF0ZWQgdG8gZGV0ZXJtaW5lIG9ic2VydmF0aW9ucyBhcmUgY2xhc3NpZmllZCBhcyBvYmVzZS4gQWNjb3JkaW5nIHRvIHRoZSBDREMgKGh0dHBzOi8vd3d3LmNkYy5nb3YvbmNjZHBocC9kbnBhby9kYXRhLXRyZW5kcy1tYXBzL2hlbHAvbnBhb19kdG0vZGVmaW5pdGlvbnMuaHRtbCksIEFkdWx0IG9iZXNpdHkgaXMgZGVmaW5lZCBhcyBib2R5IG1hc3MgaW5kZXggKEJNSSkg4omlIDMwLjAuIEFub3RoZXIgZmVhdHVyZSBuYW1lZCBvYmVzZSwgYmFzZWQgb24gQk1JLCBpcyBjcmVhdGVkLCB3aGVyZSAwID0gbm8gYW5kIDEgPSB5ZXMuIAoKYGBge3J9CgojIENyZWF0ZSBCTUkgZmVhdHVyZQppbXB1dGVkX25oYW5lcyRCTUkgPC0gKGltcHV0ZWRfbmhhbmVzJGJvZHkud2VpZ2h0ICogNzAzKSAvIChpbXB1dGVkX25oYW5lcyRoZWlnaHQgKiBpbXB1dGVkX25oYW5lcyRoZWlnaHQpCiMgUm91bmQgdGhlIEJNSSB0byB0aGUgbmVhcmVzdCBvbmVzIHBsYWNlCmltcHV0ZWRfbmhhbmVzJEJNSSA8LSByb3VuZChpbXB1dGVkX25oYW5lcyRCTUksIDApCgojIENyZWF0ZSB0aGUgb2Jlc2UgZmVhdHVyZSBiYXNlZCBvbiBCTUkKaW1wdXRlZF9uaGFuZXMkb2Jlc2UgPC0gaWZlbHNlKGltcHV0ZWRfbmhhbmVzJEJNSSA+PSAzMCwgMSwgMCkKCmBgYAoKYGBge3J9CgpwYXIobWZyb3cgPSBjKDEsIDIpKQoKIyBIaXN0b2dyYW0gZm9yIEJNSQoKaGlzdChpbXB1dGVkX25oYW5lcyRCTUksIAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIEJNSSIsIAogICAgIHhsYWIgPSAiQk1JIiwgCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsIAogICAgIGJvcmRlciA9ICJibGFjayIsIAogICAgIGJyZWFrcyA9IDIwKQoKIyBiYXIgY2hhcnQgZm9yIG9iZXNlCm5oYW5lcyRvYmVzZSA8LSBmYWN0b3IoaW1wdXRlZF9uaGFuZXMkb2Jlc2UsIGxldmVscyA9IGMoMCwgMSksIGxhYmVscyA9IGMoIk5vIiwgIlllcyIpKQoKb2Jlc2VfY291bnQgPC0gdGFibGUoaW1wdXRlZF9uaGFuZXMkb2Jlc2UpCgpiYXJwbG90KGhlaWdodCA9IG9iZXNlX2NvdW50LCBjb2wgPSAic3RlZWxibHVlIiwgbWFpbiA9ICJPYmVzaXR5IiwgeGxhYiA9ICJPYmVzaXR5IiwgeWxhYiA9ICJDb3VudCIpCgpgYGAKCgpgYGB7cn0KIyBTZXQgdXAgYSAyeDIgcGxvdHRpbmcgZ3JpZApwYXIobWZyb3cgPSBjKDIsIDIpKQoKCiMgQ3JlYXRlIGEgY29udGluZ2VuY3kgdGFibGUKb2Jlc2VfaGJwX3RhYmxlIDwtIHRhYmxlKGltcHV0ZWRfbmhhbmVzJG9iZXNlLCBpbXB1dGVkX25oYW5lcyRoYnApCgojIEdlbmVyYXRlIG1vc2FpYyBwbG90Cm1vc2FpY3Bsb3Qob2Jlc2VfaGJwX3RhYmxlLCAKICAgICAgICAgICBjb2wgPSBjKCJnb2xkIiwgInJveWFsYmx1ZSIpLCAKICAgICAgICAgICBtYWluID0gIk9iZXNpdHkgYW5kIEhpZ2ggQmxvb2QgUHJlc3N1cmUiLAogICAgICAgICAgIHhsYWIgPSAiT2Jlc2l0eSBTdGF0dXMiLCAKICAgICAgICAgICB5bGFiID0gIkhCUCBTdGF0dXMiLAogICAgICAgICAgIGJvcmRlciA9ICJibGFjayIpCgojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlCm9iZXNlX3NleF90YWJsZSA8LSB0YWJsZShpbXB1dGVkX25oYW5lcyRzZXgsIGltcHV0ZWRfbmhhbmVzJG9iZXNlKQoKIyBHZW5lcmF0ZSBtb3NhaWMgcGxvdAptb3NhaWNwbG90KG9iZXNlX3NleF90YWJsZSwgCiAgICAgICAgICAgY29sID0gYygiZ29sZCIsICJyb3lhbGJsdWUiKSwgCiAgICAgICAgICAgbWFpbiA9ICJTZXggYW5kIE9iZXNpdHkiLAogICAgICAgICAgIHhsYWIgPSAiT2Jlc2l0eSBTdGF0dXMiLCAKICAgICAgICAgICB5bGFiID0gIlNleCIsCiAgICAgICAgICAgYm9yZGVyID0gImJsYWNrIikKCgojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlCm9iZXNlX3JhY2VfdGFibGUgPC0gdGFibGUoaW1wdXRlZF9uaGFuZXMkcmFjZSwgaW1wdXRlZF9uaGFuZXMkb2Jlc2UpCgojIEdlbmVyYXRlIG1vc2FpYyBwbG90Cm1vc2FpY3Bsb3Qob2Jlc2VfcmFjZV90YWJsZSwgCiAgICAgICAgICAgY29sID0gYygiZ29sZCIsICJyb3lhbGJsdWUiKSwgCiAgICAgICAgICAgbWFpbiA9ICJSYWNlIGFuZCBPYmVzaXR5IiwKICAgICAgICAgICB4bGFiID0gIk9iZXNpdHkgU3RhdHVzIiwgCiAgICAgICAgICAgeWxhYiA9ICJSYWNlIiwKICAgICAgICAgICBib3JkZXIgPSAiYmxhY2siKQoKCiMgQ3JlYXRlIGEgY29udGluZ2VuY3kgdGFibGUKb2Jlc2Vfc21va2VfdGFibGUgPC0gdGFibGUoaW1wdXRlZF9uaGFuZXMkY3VycmVudC5zbWssIGltcHV0ZWRfbmhhbmVzJG9iZXNlKQoKIyBHZW5lcmF0ZSBtb3NhaWMgcGxvdAptb3NhaWNwbG90KG9iZXNlX3Ntb2tlX3RhYmxlLCAKICAgICAgICAgICBjb2wgPSBjKCJnb2xkIiwgInJveWFsYmx1ZSIpLCAKICAgICAgICAgICBtYWluID0gIkN1cnJlbnQgU21va2luZyBTdGF0dXMgYW5kIE9iZXNpdHkiLAogICAgICAgICAgIHhsYWIgPSAiT2Jlc2l0eSBTdGF0dXMiLCAKICAgICAgICAgICB5bGFiID0gIkN1cnJlbnQgU21va2luZyBTdGF0dXMiLAogICAgICAgICAgIGJvcmRlciA9ICJibGFjayIpCgoKCmBgYAoKCgoKCiMjIE11bHRpcGxlIEltcHV0YXRpb24KCk11bHRpcGxlIEltcHV0YXRpb24gYnkgQ2hhaW5lZCBFcXVhdGlvbnMgKE1JQ0UpIGlzIGEgcG93ZXJmdWwgdGVjaG5pcXVlIHVzZWQgdG8gaGFuZGxlIG1pc3NpbmcgZGF0YSBieSBjcmVhdGluZyBtdWx0aXBsZSBwbGF1c2libGUgaW1wdXRhdGlvbnMgZm9yIGVhY2ggbWlzc2luZyB2YWx1ZSBiYXNlZCBvbiByZWxhdGlvbnNoaXBzIGluIHRoZSBvYnNlcnZlZCBkYXRhLiBGb3IgYm9keSB3ZWlnaHQsIGNob2xlc3Rlcm9sLCBhbmQgcmFjZSwgTUlDRSBjYW4gYmUgYXBwbGllZCB0byBnZW5lcmF0ZSBhY2N1cmF0ZSBpbXB1dGF0aW9ucywgYWNjb3VudGluZyBmb3IgY29ycmVsYXRpb25zIGJldHdlZW4gdGhlc2UgdmFyaWFibGVzIGFuZCBvdGhlciBmZWF0dXJlcyBpbiB0aGUgZGF0YXNldC4gVGhpcyBtZXRob2QgYWxsb3dzIGZvciBtb3JlIHJlbGlhYmxlIHN0YXRpc3RpY2FsIGFuYWx5c2VzIGJ5IHJlZHVjaW5nIGJpYXMgYW5kIHByZXNlcnZpbmcgdmFyaWFiaWxpdHkgaW4gdGhlIGltcHV0ZWQgdmFsdWVzLCBtYWtpbmcgaXQgZXNwZWNpYWxseSB1c2VmdWwgZm9yIGhhbmRsaW5nIG1pc3NpbmcgZGF0YSBpbiBpbmNvbWluZyBuZXcgZGF0YSBzZXRzLgoKYGBge3J9CgpuaGFuZXMxJHJhY2UgPC0gYXMuZmFjdG9yKG5oYW5lczEkcmFjZSkgIApuaGFuZXMxJGNob2xlc3Ryb2wgPC0gYXMubnVtZXJpYyhuaGFuZXMxJGNob2xlc3Ryb2wpCm5oYW5lczEkYm9keS53ZWlnaHQgPC0gYXMubnVtZXJpYyhuaGFuZXMxJGJvZHkud2VpZ2h0KQoKbmhhbmVzMiA8LSBuaGFuZXMxCgoKIyAxIGluaXRpYWxpemF0aW9uCgppbml0IDwtIG1pY2UobmhhbmVzMiwgbWF4aXQ9MCkKcHJpbnQoaW5pdCRtZXRob2QpCgoKIyAyIGltcHV0YXRpb24gbW9kZWxzCgppbXAgPC0gbWljZShuaGFuZXMyLCBtZXRob2QgPSBjKCIiLCAiIiwgIiIsICIiLCAiIiwgIiIsICIiLCAicG9seXJlZyIsICJwbW0iLCAiIiwgIiIsICIiLCAiIiwgIiIsICJwbW0iLCAiIiksIAogICAgICAgICAgICBtYXhpdD0xMCwKICAgICAgICAgICAgbT0xLAogICAgICAgICAgICBzZWVkID0gMTIzLAogICAgICAgICAgICBwcmludD1GKQoKY29tcGxldGUoaW1wLCBhY3Rpb24gPSAxKVsxOjEwLF0KCgojIDMgbXVsdGlwbGUgKGl0ZXJhdGl2ZSkgaW1wdXRhdGlvbnMKCmltcDUgPC0gbWljZShuaGFuZXMyLCBtZXRob2QgPSAicG1tIiwgbSA9IDUsIG1heGl0ID0gMTAsIHNlZWQgPSAxMjMsIHByaW50PUYpCnBsb3QoaW1wNSkKCgpgYGAKCgpNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zIChNSUNFKSB3aWxsIGJlIGFwcGxpZWQgdG8gaGFuZGxlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBib2R5IHdlaWdodCB2YXJpYWJsZS4gVG8gYXNzZXNzIHRoZSBxdWFsaXR5IG9mIHRoZSBpbXB1dGF0aW9ucywgdGhlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSB3aWxsIGJlIGNhbGN1bGF0ZWQgYW5kIGNvbXBhcmVkIHRoZSBNU0Ugb2YgdGhlIHNpbmdsZSBpbXB1dGF0aW9uIGNvbmR1Y3RlZCBmb3IgYm9keSB3ZWlnaHQgZWFybGllciBpbiB0aGlzIGFuYWx5c2lzLCBwcm92aWRpbmcgYSBtZWFzdXJlIG9mIHRoZSBpbXB1dGF0aW9uIGFjY3VyYWN5LgoKCmBgYHtyfQptb2RlbDVfYm9keXdlaWdodCA8LSB3aXRoKGltcDUsIGxtKGJvZHkud2VpZ2h0IH4gaGVpZ2h0ICsgYXZnLmRicCArIHNleCkpCnN1bW1hcnkuc3RhdHMgPSBzdW1tYXJ5KG1vZGVsNV9ib2R5d2VpZ2h0KQoKCnN1bW1hcnkocG9vbChtb2RlbDVfYm9keXdlaWdodCkpCgpiZXRhID0gc3VtbWFyeS5zdGF0cyRlc3RpbWF0ZVtzZXEoMSwxNSxieT0zKV0gICMgZXhwbGljaXQgdmVjdG9yOiBjKDEsNCw3LDEwLDEzKQpiZXRhLnZhciA9IChzdW1tYXJ5LnN0YXRzJHN0ZC5lcnJvcltzZXEoMSwxNSxieT0zKV0pXjIgClEgPSBtZWFuKGJldGEpClUgPSBtZWFuKGJldGEudmFyKQpCID0gdmFyKGJldGEpClQgPSBVICsgKDYvNSkqQgpwb29sLnNlID0gc3FydChUKQpjYmluZChwb29sLnNlLmludGVyY2VwdCA9IHBvb2wuc2UpCgpgYGAKVGhlIG1vZGVsIGNhbiBiZSB1c2VkIHRvIHByZWRpY3QgYm9keSB3ZWlnaHQgZm9yIGluY29taW5nIG5ldyBkYXRhLiAKClRoZSBNU0UgZm9yIHNpbmdsZSBib2R5IHdlaWdodCBpbXB1dGF0aW9uIHVzZWQgZWFybGllciBpbiB0aGlzIGFuYWx5c2lzIGlzIE1TRSA9IChSU0UpXjIgPSAoMzMuNTIpXjIgPSAxMTIzLjYgd2hpY2ggaXMgc3Vic3RhbnRpYWxseSBoaWdoZXIgdGhhbiBNU0Ugd2hlbiB1c2luZyBNSUNFLiBJbiB0dXJuLCBNSUNFIGlzIHJlY29tbWVuZGVkIGluIHRoaXMgYW5hbHlzaXMuIAoKCgpNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zIChNSUNFKSB3aWxsIGJlIGFwcGxpZWQgdG8gaGFuZGxlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBjaG9sZXN0ZXJvbCB2YXJpYWJsZS4gVG8gYXNzZXNzIHRoZSBxdWFsaXR5IG9mIHRoZSBpbXB1dGF0aW9ucywgdGhlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSB3aWxsIGJlIGNhbGN1bGF0ZWQgYW5kIGNvbXBhcmVkIHRoZSBNU0Ugb2YgdGhlIHNpbmdsZSBpbXB1dGF0aW9uIGNvbmR1Y3RlZCBmb3IgY2hvbGVzdGVyb2wgZWFybGllciBpbiB0aGlzIGFuYWx5c2lzLCBwcm92aWRpbmcgYSBtZWFzdXJlIG9mIHRoZSBpbXB1dGF0aW9uIGFjY3VyYWN5LgoKYGBge3J9Cm1vZGVsNV9jaG9sIDwtIHdpdGgoaW1wNSwgbG0oY2hvbGVzdHJvbCB+IGFnZSArIGF2Zy5zYnApKQpzdW1tYXJ5LnN0YXRzID0gc3VtbWFyeShtb2RlbDVfY2hvbCkKCgpzdW1tYXJ5KHBvb2wobW9kZWw1X2Nob2wpKQoKYmV0YSA9IHN1bW1hcnkuc3RhdHMkZXN0aW1hdGVbc2VxKDEsMTUsYnk9MyldICAjIGV4cGxpY2l0IHZlY3RvcjogYygxLDQsNywxMCwxMykKYmV0YS52YXIgPSAoc3VtbWFyeS5zdGF0cyRzdGQuZXJyb3Jbc2VxKDEsMTUsYnk9MyldKV4yIApRID0gbWVhbihiZXRhKQpVID0gbWVhbihiZXRhLnZhcikKQiA9IHZhcihiZXRhKQpUID0gVSArICg2LzUpKkIKcG9vbC5zZSA9IHNxcnQoVCkKY2JpbmQocG9vbC5zZS5pbnRlcmNlcHQgPSBwb29sLnNlKQoKCmBgYApUaGUgbW9kZWwgY2FuIGJlIHVzZWQgdG8gcHJlZGljdCBjaG9sZXN0ZXJvbCBmb3IgaW5jb21pbmcgbmV3IGRhdGEuIAoKVGhlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBmcm9tIHRoZSBSZXNpZHVhbCBTdGFuZGFyZCBFcnJvciAoUlNFKSBnaXZlbiBpbiB0aGUgbW9kZWwgb3V0cHV0IGZyb20gdGhlIGNob2xlc3Rlcm9sIHNpbmdsZSBpbXB1dGF0aW9uIG1ldGhvZCB1c2VkIGVhcmx5IGluIHRoaXMgYW5hbHlzaXMgaXMgTVNFID0gKDQyLjU5KV4yID0gMTgxMy45LiBUaHVzLCB0aGUgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpID0gMTgxMy45LgoKVGhlIE1TRSB1c2luZyBNSUNFIGlzIDMuNjYgd2hpY2ggaXMgc3Vic3RhbnRpYWxseSBsZXNzLiBUaGVyZWZvcmUsIE1JQ0UgaXMgcmVjb21tZW5kZWQgaW4gdGhlIGFuYWx5c2lzIG9mIHRoaXMgZGF0YSBzZXQuIAoKCgpGb3IgdGhlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSByYWNlIHZhcmlhYmxlLCBNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zIChNSUNFKSB3aWxsIGJlIGFwcGxpZWQgdXNpbmcgbXVsdGlub21pYWwgbG9naXN0aWMgcmVncmVzc2lvbi4gVGhpcyBtZXRob2Qgd2lsbCBhY2NvdW50IGZvciB0aGUgY2F0ZWdvcmljYWwgbmF0dXJlIG9mIHRoZSByYWNlIHZhcmlhYmxlLCBhbGxvd2luZyBmb3IgYXBwcm9wcmlhdGUgaW1wdXRhdGlvbiBiYXNlZCBvbiB0aGUgcmVsYXRpb25zaGlwcyB3aXRoIG90aGVyIGZlYXR1cmVzIGluIHRoZSBkYXRhc2V0LgoKYGBge3J9Cgptb2RlbF9yYWNlIDwtIHdpdGgoaW1wNSwgbXVsdGlub20ocmFjZSB+IGF2Zy5kYnAgKyBhdmcuc2JwICsgY2hvbGVzdHJvbCArIGhicCkpCgojIFBvb2wgdGhlIHJlc3VsdHMgZnJvbSB0aGUgaW1wdXRlZCBkYXRhc2V0cwpwb29sZWRfcmFjZSA8LSBwb29sKG1vZGVsX3JhY2UpCgojIEdldCBhIHN1bW1hcnkgb2YgdGhlIHBvb2xlZCByZXN1bHRzCnN1bW1hcnkocG9vbGVkX3JhY2UpCgpgYGAKClRoZSBtb2RlbCBjYW4gYmUgdXNlZCB0byBwcmVkaWN0IHJhY2UgZm9yIGluY29taW5nIG5ldyBkYXRhLiAKCgojIEZFQVRVUkUgVFJBTlNGT1JNSU5HCgpCYXNlZCBvbiB0aGUgZGlzdHJpYnV0aW9uIHBhdHRlcm5zIG9ic2VydmVkIGluIHRoZSBOSEFORVMgZGF0YSBzZXQsIHNldmVyYWwgZmVhdHVyZSBlbmdpbmVlcmluZyB0YXNrcyBjYW4gYmUgcGVyZm9ybWVkIHRvIGVuaGFuY2UgdGhlIGRhdGHigJlzIHVzYWJpbGl0eSBmb3IgbW9kZWxpbmcuIEZpcnN0LCBmb3IgYWdlLCB3aGljaCBpcyByb3VnaGx5IHVuaWZvcm0sIG5vIG1ham9yIHRyYW5zZm9ybWF0aW9uIGlzIG5lZWRlZCwgdGhvdWdoIHdlIG1heSBjb25zaWRlciBiaW5uaW5nIGl0IGludG8gYWdlIGdyb3VwcyBpZiBpdCBoZWxwcyB3aXRoIGludGVycHJldGFiaWxpdHkuIEZvciB3ZWlnaHQgYW5kIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIChCUCksIHdoaWNoIGFyZSBib3RoIHNrZXdlZCByaWdodCwgYSBsb2cgdHJhbnNmb3JtYXRpb24gY291bGQgYmUgYXBwbGllZCB0byByZWR1Y2Ugc2tld25lc3MgYW5kIG1ha2UgdGhlc2UgdmFyaWFibGVzIG1vcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIGltcHJvdmluZyBtb2RlbCBwZXJmb3JtYW5jZS4gU2ltaWxhcmx5LCBmb3IgY2hvbGVzdGVyb2wsIHdoaWNoIGlzIHNsaWdodGx5IHNrZXdlZCByaWdodCwgYSBsb2cgdHJhbnNmb3JtYXRpb24gbWF5IGFsc28gYmUgdXNlZnVsLiBGb3IgZGlhc3RvbGljIEJQLCB3aGljaCBpcyByb3VnaGx5IHN5bW1ldHJpYywgbm8gdHJhbnNmb3JtYXRpb24gaXMgbmVjZXNzYXJ5LCBidXQgY2hlY2tpbmcgZm9yIG91dGxpZXJzIGNvdWxkIGJlIGhlbHBmdWwuIFRoZXNlIHRyYW5zZm9ybWF0aW9ucyBhaW0gdG8gc3RhYmlsaXplIHZhcmlhbmNlLCByZWR1Y2UgdGhlIGltcGFjdCBvZiBleHRyZW1lIHZhbHVlcywgYW5kIGNyZWF0ZSBmZWF0dXJlcyB0aGF0IGFyZSBtb3JlIHN1aXRhYmxlIGZvciBtb2RlbGluZyB3aXRoIHRlY2huaXF1ZXMgbGlrZSBsaW5lYXIgcmVncmVzc2lvbiBvciBvdGhlciBzdGF0aXN0aWNhbCBtZXRob2RzLgoKCmBgYHtyfQojTG9nIHRyYW5zZm9ybWF0aW9uIHRvIHJlZHVjZSBza2V3CgppbXB1dGVkX25oYW5lcyRsb2dfYm9keS53ZWlnaHQgPC0gbG9nKGltcHV0ZWRfbmhhbmVzJGJvZHkud2VpZ2h0KQppbXB1dGVkX25oYW5lcyRsb2dfYXZnLnNicCA8LSBsb2coaW1wdXRlZF9uaGFuZXMkYXZnLnNicCkKaW1wdXRlZF9uaGFuZXMkbG9nX2Nob2xlc3Ryb2wgPC0gbG9nKGltcHV0ZWRfbmhhbmVzJGNob2xlc3Ryb2wpCmltcHV0ZWRfbmhhbmVzJGxvZ19CTUkgPC0gbG9nKGltcHV0ZWRfbmhhbmVzJEJNSSkKCgoKCiMgU2V0IHVwIHRoZSBwbG90dGluZyBhcmVhIHRvIHNob3cgMngyIGdyaWQgb2YgaGlzdG9ncmFtcwpwYXIobWZyb3cgPSBjKDIsIDIpKSAgIyBUaGlzIHdpbGwgY3JlYXRlIGEgMngyIGdyaWQgb2YgcGxvdHMKCiMgSGlzdG9ncmFtIGZvciBMb2ctQ2hvbGVzdGVyb2wKaGlzdChpbXB1dGVkX25oYW5lcyRsb2dfY2hvbGVzdHJvbCwgCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgTG9nKENob2xlc3Rlcm9sKSIsIAogICAgIHhsYWIgPSAiTG9nKENob2xlc3Rlcm9sKSIsIAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBib3JkZXIgPSAiYmxhY2siLCAKICAgICBicmVha3MgPSAyMCkgIAogICAgCiMgSGlzdG9ncmFtIGZvciBMb2ctQm9keSBXZWlnaHQKaGlzdChpbXB1dGVkX25oYW5lcyRsb2dfYm9keS53ZWlnaHQsIAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIExvZyhCb2R5IFdlaWdodCkiLCAKICAgICB4bGFiID0gIkxvZyhCb2R5IFdlaWdodCkiLCAKICAgICBjb2wgPSAic3RlZWxibHVlIiwgCiAgICAgYm9yZGVyID0gImJsYWNrIiwgCiAgICAgYnJlYWtzID0gMjApIAoKCiMgSGlzdG9ncmFtIGZvciBMb2ctU3lzdG9saWMgQlAKaGlzdChpbXB1dGVkX25oYW5lcyRsb2dfYXZnLnNicCwgCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgTG9nKFN5c3RvbGljIEJQKSIsIAogICAgIHhsYWIgPSAiTG9nKFN5c3RvbGljIEJQKSIsIAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBib3JkZXIgPSAiYmxhY2siLCAKICAgICBicmVha3MgPSAyMCkKICAgCgojIEhpc3RvZ3JhbSBmb3IgTG9nLUJNSQpoaXN0KGltcHV0ZWRfbmhhbmVzJGxvZ19CTUksIAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIExvZyhCTUkpIiwgCiAgICAgeGxhYiA9ICJMb2coQk1JKSIsIAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBib3JkZXIgPSAiYmxhY2siLCAKICAgICBicmVha3MgPSAyMCkKICAgIAoKCmBgYAoKVGhlIGRpc3RyaWJ1dGlvbnMgb2YgdGhlc2UgcHJldmlvdXNseSBza2V3ZWQgZmVhdHVyZXMgYXJlIG1vcmUgc3ltbWV0cmljIGFmdGVyIGxvZyB0cmFuc2Zvcm1hdGlvbnMuIAoKCiMgU1RBTkRBUkRJWklORwoKU3RhbmRhcmRpemluZyBmZWF0dXJlcyBlbnN1cmVzIHRoYXQgdmFyaWFibGVzIHdpdGggZGlmZmVyZW50IHNjYWxlcywgc3VjaCBhcyBhZ2UsIEJNSSwgaGVpZ2h0LCBhbmQgY2hvbGVzdGVyb2wsIGNvbnRyaWJ1dGUgZXF1YWxseSB0byB0aGUgYW5hbHlzaXMuIFdpdGhvdXQgc3RhbmRhcmRpemF0aW9uLCB2YXJpYWJsZXMgd2l0aCBsYXJnZXIgcmFuZ2VzIGNvdWxkIGRvbWluYXRlIG1vZGVsIHBlcmZvcm1hbmNlLCBsZWFkaW5nIHRvIGJpYXNlZCByZXN1bHRzLiBCeSBzdGFuZGFyZGl6aW5nIHRoZXNlIGZlYXR1cmVzLCB3ZSBpbXByb3ZlIG1vZGVsIGFjY3VyYWN5LCBlbmFibGUgYmV0dGVyIGNvbXBhcmlzb24gYmV0d2VlbiB2YXJpYWJsZXMsIGFuZCBlbmhhbmNlIHRoZSBlZmZpY2llbmN5IG9mIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcywgcGFydGljdWxhcmx5IGluIG1vZGVscyB0aGF0IHJlbHkgb24gZGlzdGFuY2UtYmFzZWQgb3IgZ3JhZGllbnQtYmFzZWQgbWV0aG9kcy4KCgpgYGB7cn0KCiMgU3BlY2lmeSB0aGUgY29sdW1ucyB0byBiZSBzdGFuZGFyZGl6ZWQKY29sdW1uc190b19zdGFuZGFyZGl6ZSA8LSBjKCJhZ2UiLCAiQk1JIiwgImhlaWdodCIsICJib2R5LndlaWdodCIsICJjaG9sZXN0cm9sIiwgImF2Zy5kYnAiLCAiYXZnLnNicCIpCgojIFN0YW5kYXJkaXplIHRoZSBzZWxlY3RlZCBjb2x1bW5zCmltcHV0ZWRfbmhhbmVzW2NvbHVtbnNfdG9fc3RhbmRhcmRpemVdIDwtIHNjYWxlKGltcHV0ZWRfbmhhbmVzW2NvbHVtbnNfdG9fc3RhbmRhcmRpemVdKQoKCmBgYAoKCiMgRkVBVFVSRSBTRUxFQ1RJT04KCkZlYXR1cmUgc2VsZWN0aW9uIGlzIGEgY3JpdGljYWwgc3RlcCBpbiBtb2RlbCBidWlsZGluZywgYXMgaXQgaGVscHMgaW1wcm92ZSBtb2RlbCBwZXJmb3JtYW5jZSBieSBpZGVudGlmeWluZyB0aGUgbW9zdCByZWxldmFudCBwcmVkaWN0b3JzIGFuZCByZWR1Y2luZyBvdmVyZml0dGluZy4gSW4gdGhpcyBhbmFseXNpcywgYm90aCBtb2RlbC1iYXNlZCBzZWxlY3Rpb24gYW5kIHJlZ3VsYXJpemF0aW9uLWJhc2VkIHNlbGVjdGlvbiB0ZWNobmlxdWVzIHdpbGwgYmUgdXNlZCB0byBhc3Nlc3MgYW5kIHJldGFpbiB0aGUgbW9zdCBpbmZsdWVudGlhbCBmZWF0dXJlcyBmb3IgYWNjdXJhdGUgcHJlZGljdGlvbiwgZW5zdXJpbmcgYSBtb3JlIGVmZmljaWVudCBhbmQgaW50ZXJwcmV0YWJsZSBtb2RlbC4KCgpBIGZ1bGwgbW9kZWwgaXMgY3JlYXRlZCB0byBwcmVkaWN0IGNob2xlc3Rlcm9sLgoKYGBge3J9Cgojc2FwcGx5KGltcHV0ZWRfbmhhbmVzLCBmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHgpKSkKCiNoZWFkKGltcHV0ZWRfbmhhbmVzLCAyNSkKCgojaGVhZChpbXB1dGVkX25oYW5lcykKCgptb2RlbF9mdWxsIDwtIGxtKGNob2xlc3Ryb2wgfiBhZ2UgKyByYWNlICsgQk1JICsgaGVpZ2h0ICsgYm9keS53ZWlnaHQgKyAKICAgICAgICAgICAgICAgICAgaGJwICsgYXZnLmRicCArIGF2Zy5zYnAgKyBvYmVzZSwgCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBpbXB1dGVkX25oYW5lcykKCiMgU3VtbWFyeSBvZiB0aGUgZnVsbCBtb2RlbApzdW1tYXJ5KG1vZGVsX2Z1bGwpCgpgYGAKClN0ZXB3aXNlIG1vZGVsIHNlbGVjdGlvbiBpcyBjb25kdWN0ZWQgdG8gcmV0YWluIHNpZ25pZmljYW50IHByZWRpY3RvcnMuIFNlbGVjdGlvbiBwcm9jZXNzZXMgYmFzZWQgb24gQUlDIGFuZCBCSUMgYXJlIGNvbmR1Y3RlZC4gCgpgYGB7cn0KIyBTdGVwd2lzZSBtb2RlbCBzZWxlY3Rpb24gYmFzZWQgb24gQUlDCm1vZGVsX3N0ZXB3aXNlX0FJQyA8LSBzdGVwKG1vZGVsX2Z1bGwsIGRpcmVjdGlvbiA9ICJib3RoIiwgdHJhY2UgPSAwKQoKIyBTdGVwd2lzZSBtb2RlbCBzZWxlY3Rpb24gYmFzZWQgb24gQklDCm1vZGVsX3N0ZXB3aXNlX0JJQyA8LSBzdGVwKG1vZGVsX2Z1bGwsIGRpcmVjdGlvbiA9ICJib3RoIiwgayA9IGxvZyhucm93KGltcHV0ZWRfbmhhbmVzKSksIHRyYWNlID0gMCkKCiMgRGlzcGxheSB0aGUgc3VtbWFyeSBvZiB0aGUgc2VsZWN0ZWQgbW9kZWwKc3VtbWFyeShtb2RlbF9zdGVwd2lzZV9BSUMpCnN1bW1hcnkobW9kZWxfc3RlcHdpc2VfQklDKQoKYGBgCgoKCmBgYHtyfQojIENoZWNrIFItc3F1YXJlZCB2YWx1ZQpzdW1tYXJ5KG1vZGVsX3N0ZXB3aXNlX0FJQykkci5zcXVhcmVkCnN1bW1hcnkobW9kZWxfc3RlcHdpc2VfQklDKSRyLnNxdWFyZWQKCgpgYGAKCkJvdGggQUlDIGFuZCBCSUMgc3RlcHdpc2UgbW9kZWwgc2VsZWN0aW9uIHByb2Nlc3NlcyBoYXZlIHNpbWlsYXIgUiBzcXVhcmVkIHZhbHVlcy4gVGhlIEJJQyBzdGVwd2lzZSBtb2RlbCBpcyBwYXJzaW1vbmlvdXMuIFRoaXMgbW9kZWwgc2VsZWN0cyBhZ2UsIGhlaWdodCwgYm9keSB3ZWlnaHQsIGFuZCBhdmVyYWdlIGRpYXN0b2xpYyBibG9vZCBwcmVzc3VyZSBhcyBwcmVkaWN0b3JzIGZvciBjaG9sZXN0ZXJvbC4gCgoKClJlZ3VsYXJpemF0aW9uLWJhc2VkIHNlbGVjdGlvbiB0ZWNobmlxdWVzIGFyZSB1c2VmdWwgZm9yIGltcHJvdmluZyBtb2RlbCBwZXJmb3JtYW5jZSBhbmQgcHJldmVudGluZyBvdmVyZml0dGluZywgZXNwZWNpYWxseSB3aGVuIHlvdSBoYXZlIG1hbnkgcHJlZGljdG9ycy4gVHdvIGNvbW1vbiByZWd1bGFyaXphdGlvbiBtZXRob2RzIGFyZSBSaWRnZSByZWdyZXNzaW9uIGFuZCBMYXNzbyByZWdyZXNzaW9uLgoKYGBge3J9CiMgRXh0cmFjdCB0aGUgcmVzcG9uc2UgYW5kIHByZWRpY3RvcnMKeSA8LSBpbXB1dGVkX25oYW5lcyRjaG9sZXN0cm9sICAjIFJlc3BvbnNlIHZhcmlhYmxlClggPC0gYXMubWF0cml4KGltcHV0ZWRfbmhhbmVzWywgYygiYWdlIiwgIkJNSSIsICJoZWlnaHQiLCAiYm9keS53ZWlnaHQiLCAiYXZnLmRicCIsICJhdmcuc2JwIildKQoKIyBPcHRpb25hbDogU2NhbGUgcHJlZGljdG9ycyAocmVjb21tZW5kZWQgZm9yIFJpZGdlIGFuZCBMYXNzbykKWF9zY2FsZWQgPC0gc2NhbGUoWCkKCnJpZGdlX21vZGVsIDwtIGdsbW5ldChYX3NjYWxlZCwgeSwgYWxwaGEgPSAwKSAgIyBhbHBoYSA9IDAgZm9yIFJpZGdlCgpsYXNzb19tb2RlbCA8LSBnbG1uZXQoWF9zY2FsZWQsIHksIGFscGhhID0gMSkgICMgYWxwaGEgPSAxIGZvciBMYXNzbwoKIyBSaWRnZSBjcm9zcy12YWxpZGF0aW9uCmN2X3JpZGdlIDwtIGN2LmdsbW5ldChYX3NjYWxlZCwgeSwgYWxwaGEgPSAwKQpwbG90KGN2X3JpZGdlKQpiZXN0X2xhbWJkYV9yaWRnZSA8LSBjdl9yaWRnZSRsYW1iZGEubWluCgojIExhc3NvIGNyb3NzLXZhbGlkYXRpb24KY3ZfbGFzc28gPC0gY3YuZ2xtbmV0KFhfc2NhbGVkLCB5LCBhbHBoYSA9IDEpCnBsb3QoY3ZfbGFzc28pCmJlc3RfbGFtYmRhX2xhc3NvIDwtIGN2X2xhc3NvJGxhbWJkYS5taW4KCgojIFJpZGdlIGNvZWZmaWNpZW50cwpyaWRnZV9jb2VmZmljaWVudHMgPC0gY29lZihjdl9yaWRnZSwgcyA9ICJsYW1iZGEubWluIikKcHJpbnQocmlkZ2VfY29lZmZpY2llbnRzKQoKIyBMYXNzbyBjb2VmZmljaWVudHMKbGFzc29fY29lZmZpY2llbnRzIDwtIGNvZWYoY3ZfbGFzc28sIHMgPSAibGFtYmRhLm1pbiIpCnByaW50KGxhc3NvX2NvZWZmaWNpZW50cykKCgojIFByZWRpY3Rpb25zIGZvciBSaWRnZQpwcmVkX3JpZGdlIDwtIHByZWRpY3QoY3ZfcmlkZ2UsIFhfc2NhbGVkLCBzID0gImxhbWJkYS5taW4iKQojIFByZWRpY3Rpb25zIGZvciBMYXNzbwpwcmVkX2xhc3NvIDwtIHByZWRpY3QoY3ZfbGFzc28sIFhfc2NhbGVkLCBzID0gImxhbWJkYS5taW4iKQoKIyBDYWxjdWxhdGUgUk1TRSBvciBvdGhlciBtZXRyaWNzIHRvIGFzc2VzcyBtb2RlbCBwZXJmb3JtYW5jZQoKIyBQcmVkaWN0aW9ucyBmb3IgUmlkZ2UKcHJlZF9yaWRnZSA8LSBwcmVkaWN0KGN2X3JpZGdlLCBYX3NjYWxlZCwgcyA9ICJsYW1iZGEubWluIikKCiMgUHJlZGljdGlvbnMgZm9yIExhc3NvCnByZWRfbGFzc28gPC0gcHJlZGljdChjdl9sYXNzbywgWF9zY2FsZWQsIHMgPSAibGFtYmRhLm1pbiIpCgojIENhbGN1bGF0ZSBSTVNFIGZvciBSaWRnZQpybXNlX3JpZGdlIDwtIHNxcnQobWVhbigocHJlZF9yaWRnZSAtIHkpXjIpKQoKIyBDYWxjdWxhdGUgUk1TRSBmb3IgTGFzc28Kcm1zZV9sYXNzbyA8LSBzcXJ0KG1lYW4oKHByZWRfbGFzc28gLSB5KV4yKSkKCiMgUHJpbnQgUk1TRSB2YWx1ZXMKY2F0KCJSaWRnZSBSTVNFOiIsIHJtc2VfcmlkZ2UsICJcbiIpCmNhdCgiTGFzc28gUk1TRToiLCBybXNlX2xhc3NvLCAiXG4iKQoKCiMgQ2FsY3VsYXRlIFLCsiBmb3IgUmlkZ2UKcnNzX3JpZGdlIDwtIHN1bSgocHJlZF9yaWRnZSAtIHkpXjIpICAjIFJlc2lkdWFsIFN1bSBvZiBTcXVhcmVzCnRzc19yaWRnZSA8LSBzdW0oKHkgLSBtZWFuKHkpKV4yKSAgICAjIFRvdGFsIFN1bSBvZiBTcXVhcmVzCnIyX3JpZGdlIDwtIDEgLSByc3NfcmlkZ2UgLyB0c3NfcmlkZ2UKCiMgQ2FsY3VsYXRlIFLCsiBmb3IgTGFzc28KcnNzX2xhc3NvIDwtIHN1bSgocHJlZF9sYXNzbyAtIHkpXjIpCnRzc19sYXNzbyA8LSBzdW0oKHkgLSBtZWFuKHkpKV4yKQpyMl9sYXNzbyA8LSAxIC0gcnNzX2xhc3NvIC8gdHNzX2xhc3NvCgojIFByaW50IFLCsiB2YWx1ZXMKY2F0KCJSaWRnZSBSwrI6IiwgcjJfcmlkZ2UsICJcbiIpCmNhdCgiTGFzc28gUsKyOiIsIHIyX2xhc3NvLCAiXG4iKQoKCmBgYAoKClRoZSBSaWRnZSBhbmQgTGFzc28gbWV0aG9kIGJvdGggaGFkIHNpbWlsYXIgUiBzcXVhcmVkIHZhbHVlcy4gVGhlcmVmb3JlLCBib3RoIG1vZGVscyBhcmUgcmVjb21tZW5kZWQuIAoKCgoKCgoKCg==